diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index be2a0441f..76b69f741 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -327,6 +327,8 @@ CC0F886C1E4286FA00576FED /* ReferenceImages_64 in Resources */ = {isa = PBXBuildFile; fileRef = CC0F88691E4286FA00576FED /* ReferenceImages_64 */; }; CC0F886D1E4286FA00576FED /* ReferenceImages_iOS_10 in Resources */ = {isa = PBXBuildFile; fileRef = CC0F886A1E4286FA00576FED /* ReferenceImages_iOS_10 */; }; CC11F97A1DB181180024D77B /* ASNetworkImageNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC11F9791DB181180024D77B /* ASNetworkImageNodeTests.m */; }; + CC18248C200D49C800875940 /* ASTextNodeCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = CC18248B200D49C800875940 /* ASTextNodeCommon.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CC224E962066CA6D00BBA57F /* configuration.json in Resources */ = {isa = PBXBuildFile; fileRef = CC224E952066CA6D00BBA57F /* configuration.json */; }; CC2F65EE1E5FFB1600DA57C9 /* ASMutableElementMap.h in Headers */ = {isa = PBXBuildFile; fileRef = CC2F65EC1E5FFB1600DA57C9 /* ASMutableElementMap.h */; }; CC2F65EF1E5FFB1600DA57C9 /* ASMutableElementMap.m in Sources */ = {isa = PBXBuildFile; fileRef = CC2F65ED1E5FFB1600DA57C9 /* ASMutableElementMap.m */; }; CC3B20841C3F76D600798563 /* ASPendingStateController.h in Headers */ = {isa = PBXBuildFile; fileRef = CC3B20811C3F76D600798563 /* ASPendingStateController.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -355,6 +357,8 @@ CC58AA4B1E398E1D002C8CB4 /* ASBlockTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; CC6AA2DA1E9F03B900978E87 /* ASDisplayNode+Ancestry.h in Headers */ = {isa = PBXBuildFile; fileRef = CC6AA2D81E9F03B900978E87 /* ASDisplayNode+Ancestry.h */; settings = {ATTRIBUTES = (Public, ); }; }; CC6AA2DB1E9F03B900978E87 /* ASDisplayNode+Ancestry.m in Sources */ = {isa = PBXBuildFile; fileRef = CC6AA2D91E9F03B900978E87 /* ASDisplayNode+Ancestry.m */; }; + CC7AF196200D9BD500A21BDE /* ASExperimentalFeatures.h in Headers */ = {isa = PBXBuildFile; fileRef = CC7AF195200D9BD500A21BDE /* ASExperimentalFeatures.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CC7AF198200DAB2200A21BDE /* ASExperimentalFeatures.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7AF197200D9E8400A21BDE /* ASExperimentalFeatures.m */; }; CC7FD9E11BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7FD9E01BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m */; }; CC7FD9E21BB603FF005CCB2B /* ASPhotosFrameworkImageRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; CC84C7F220474C5300A3851B /* ASCGImageBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = CC84C7F020474C5300A3851B /* ASCGImageBuffer.h */; }; @@ -413,6 +417,12 @@ CCED5E3E2020D36800395C40 /* ASNetworkImageLoadInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = CCED5E3C2020D36800395C40 /* ASNetworkImageLoadInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; CCED5E3F2020D36800395C40 /* ASNetworkImageLoadInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = CCED5E3D2020D36800395C40 /* ASNetworkImageLoadInfo.m */; }; CCED5E412020D49D00395C40 /* ASNetworkImageLoadInfo+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = CCED5E402020D41600395C40 /* ASNetworkImageLoadInfo+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + CCEDDDCA200C2AC300FFCD0A /* ASConfigurationInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = CCEDDDC8200C2AC300FFCD0A /* ASConfigurationInternal.h */; }; + CCEDDDCB200C2AC300FFCD0A /* ASConfigurationInternal.m in Sources */ = {isa = PBXBuildFile; fileRef = CCEDDDC9200C2AC300FFCD0A /* ASConfigurationInternal.m */; }; + CCEDDDCD200C2CB900FFCD0A /* ASConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = CCEDDDCC200C2CB900FFCD0A /* ASConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CCEDDDCF200C42A200FFCD0A /* ASConfigurationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = CCEDDDCE200C42A200FFCD0A /* ASConfigurationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CCEDDDD1200C488000FFCD0A /* ASConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = CCEDDDD0200C488000FFCD0A /* ASConfiguration.m */; }; + CCEDDDD9200C518800FFCD0A /* ASConfigurationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCEDDDD8200C518800FFCD0A /* ASConfigurationTests.m */; }; CCF18FF41D2575E300DF5895 /* NSIndexSet+ASHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */; settings = {ATTRIBUTES = (Private, ); }; }; DB55C2671C641AE4004EDCF5 /* ASContextTransitioning.h in Headers */ = {isa = PBXBuildFile; fileRef = DB55C2651C641AE4004EDCF5 /* ASContextTransitioning.h */; settings = {ATTRIBUTES = (Public, ); }; }; DB7121BCD50849C498C886FB /* libPods-AsyncDisplayKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */; }; @@ -816,6 +826,8 @@ CC0F88691E4286FA00576FED /* ReferenceImages_64 */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ReferenceImages_64; sourceTree = ""; }; CC0F886A1E4286FA00576FED /* ReferenceImages_iOS_10 */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ReferenceImages_iOS_10; sourceTree = ""; }; CC11F9791DB181180024D77B /* ASNetworkImageNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASNetworkImageNodeTests.m; sourceTree = ""; }; + CC18248B200D49C800875940 /* ASTextNodeCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASTextNodeCommon.h; sourceTree = ""; }; + CC224E952066CA6D00BBA57F /* configuration.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = configuration.json; sourceTree = ""; }; CC2E317F1DAC353700EEE891 /* ASCollectionView+Undeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASCollectionView+Undeprecated.h"; sourceTree = ""; }; CC2F65EC1E5FFB1600DA57C9 /* ASMutableElementMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMutableElementMap.h; sourceTree = ""; }; CC2F65ED1E5FFB1600DA57C9 /* ASMutableElementMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASMutableElementMap.m; sourceTree = ""; }; @@ -851,6 +863,8 @@ CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBlockTypes.h; sourceTree = ""; }; CC6AA2D81E9F03B900978E87 /* ASDisplayNode+Ancestry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "ASDisplayNode+Ancestry.h"; path = "Base/ASDisplayNode+Ancestry.h"; sourceTree = ""; }; CC6AA2D91E9F03B900978E87 /* ASDisplayNode+Ancestry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "ASDisplayNode+Ancestry.m"; path = "Base/ASDisplayNode+Ancestry.m"; sourceTree = ""; }; + CC7AF195200D9BD500A21BDE /* ASExperimentalFeatures.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASExperimentalFeatures.h; sourceTree = ""; }; + CC7AF197200D9E8400A21BDE /* ASExperimentalFeatures.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASExperimentalFeatures.m; sourceTree = ""; }; CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPhotosFrameworkImageRequest.h; sourceTree = ""; }; CC7FD9DD1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPhotosFrameworkImageRequest.m; sourceTree = ""; }; CC7FD9E01BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPhotosFrameworkImageRequestTests.m; sourceTree = ""; }; @@ -919,6 +933,12 @@ CCED5E3C2020D36800395C40 /* ASNetworkImageLoadInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASNetworkImageLoadInfo.h; sourceTree = ""; }; CCED5E3D2020D36800395C40 /* ASNetworkImageLoadInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASNetworkImageLoadInfo.m; sourceTree = ""; }; CCED5E402020D41600395C40 /* ASNetworkImageLoadInfo+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ASNetworkImageLoadInfo+Private.h"; sourceTree = ""; }; + CCEDDDC8200C2AC300FFCD0A /* ASConfigurationInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASConfigurationInternal.h; sourceTree = ""; }; + CCEDDDC9200C2AC300FFCD0A /* ASConfigurationInternal.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASConfigurationInternal.m; sourceTree = ""; }; + CCEDDDCC200C2CB900FFCD0A /* ASConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASConfiguration.h; sourceTree = ""; }; + CCEDDDCE200C42A200FFCD0A /* ASConfigurationDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASConfigurationDelegate.h; sourceTree = ""; }; + CCEDDDD0200C488000FFCD0A /* ASConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASConfiguration.m; sourceTree = ""; }; + CCEDDDD8200C518800FFCD0A /* ASConfigurationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASConfigurationTests.m; sourceTree = ""; }; D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.release.xcconfig"; sourceTree = ""; }; D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = ""; }; D785F6611A74327E00291744 /* ASScrollNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASScrollNode.mm; sourceTree = ""; }; @@ -1043,6 +1063,7 @@ isa = PBXGroup; children = ( 058D09B1195D04C000B7D73C /* Source */, + CC224E942066CA6D00BBA57F /* Schemas */, 058D09C5195D04C000B7D73C /* Tests */, 058D09AE195D04C000B7D73C /* Frameworks */, 058D09AD195D04C000B7D73C /* Products */, @@ -1092,6 +1113,9 @@ CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */, DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */, DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */, + CCEDDDCC200C2CB900FFCD0A /* ASConfiguration.h */, + CCEDDDD0200C488000FFCD0A /* ASConfiguration.m */, + CCEDDDCE200C42A200FFCD0A /* ASConfigurationDelegate.h */, 055F1A3A19ABD43F004DAFF1 /* ASCellNode.h */, AC6456071B0A335000CF11B8 /* ASCellNode.mm */, CC84C7F020474C5300A3851B /* ASCGImageBuffer.h */, @@ -1123,6 +1147,8 @@ 058D09DC195D050800B7D73C /* ASDisplayNodeExtras.mm */, 0587F9BB1A7309ED00AFF0BA /* ASEditableTextNode.h */, 0587F9BC1A7309ED00AFF0BA /* ASEditableTextNode.mm */, + CC7AF195200D9BD500A21BDE /* ASExperimentalFeatures.h */, + CC7AF197200D9E8400A21BDE /* ASExperimentalFeatures.m */, 058D09DD195D050800B7D73C /* ASImageNode.h */, 058D09DE195D050800B7D73C /* ASImageNode.mm */, 68355B2E1CB5799E001D4E68 /* ASImageNode+AnimatedImage.mm */, @@ -1158,6 +1184,7 @@ 055F1A3319ABD3E3004DAFF1 /* ASTableView.mm */, AC7A2C161BDE11DF0093FE1A /* ASTableViewInternal.h */, 0574D5E119C110610097DC25 /* ASTableViewProtocols.h */, + CC18248B200D49C800875940 /* ASTextNodeCommon.h */, 058D09DF195D050800B7D73C /* ASTextNode.h */, 058D09E0195D050800B7D73C /* ASTextNode.mm */, A373200E1C571B050011FC94 /* ASTextNode+Beta.h */, @@ -1204,6 +1231,7 @@ CCDD148A1EEDCD9D0020834E /* ASCollectionModernDataSourceTests.m */, 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */, 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.mm */, + CCEDDDD8200C518800FFCD0A /* ASConfigurationTests.m */, 2911485B1A77147A005D0878 /* ASControlNodeTests.m */, 1A6C000F1FAB4ED400D05926 /* ASCornerLayoutSpecSnapshotTests.mm */, ACF6ED541B178DC700DA7C62 /* ASDimensionTests.mm */, @@ -1386,6 +1414,8 @@ 058D0A01195D050800B7D73C /* Private */ = { isa = PBXGroup; children = ( + CCEDDDC8200C2AC300FFCD0A /* ASConfigurationInternal.h */, + CCEDDDC9200C2AC300FFCD0A /* ASConfigurationInternal.m */, CCE04B2A1E313EDA006AEBBB /* Collection Data Adapter */, E52F8AEE1EAE659600B5A912 /* Collection Layout */, 6947B0BB1E36B4E30007C478 /* Layout */, @@ -1602,6 +1632,14 @@ path = Layout; sourceTree = ""; }; + CC224E942066CA6D00BBA57F /* Schemas */ = { + isa = PBXGroup; + children = ( + CC224E952066CA6D00BBA57F /* configuration.json */, + ); + path = Schemas; + sourceTree = ""; + }; CC583ABF1EF9BAB400134156 /* Common */ = { isa = PBXGroup; children = ( @@ -1777,6 +1815,7 @@ 696F01EC1DD2AF450049FBD5 /* ASEventLog.h in Headers */, 690C35641E055C7B00069B91 /* ASDimensionInternal.h in Headers */, 3917EBD41E9C2FC400D04A01 /* _ASCollectionReusableView.h in Headers */, + CC18248C200D49C800875940 /* ASTextNodeCommon.h in Headers */, 698371DB1E4379CD00437585 /* ASNodeController+Beta.h in Headers */, 6907C2581DC4ECFE00374C66 /* ASObjectDescriptionHelpers.h in Headers */, 69E0E8A71D356C9400627613 /* ASEqualityHelpers.h in Headers */, @@ -1829,6 +1868,7 @@ CC6AA2DA1E9F03B900978E87 /* ASDisplayNode+Ancestry.h in Headers */, 8021EC1D1D2B00B100799119 /* UIImage+ASConvenience.h in Headers */, B35061FD1B010EFD0018CF92 /* ASDisplayNode+Subclasses.h in Headers */, + CCEDDDCD200C2CB900FFCD0A /* ASConfiguration.h in Headers */, B35061FB1B010EFD0018CF92 /* ASDisplayNode.h in Headers */, B35061FE1B010EFD0018CF92 /* ASDisplayNodeExtras.h in Headers */, CC0F88601E4280B800576FED /* _ASCollectionViewCell.h in Headers */, @@ -1841,6 +1881,7 @@ B35062021B010EFD0018CF92 /* ASImageNode.h in Headers */, B350621F1B010EFD0018CF92 /* ASImageProtocols.h in Headers */, 34EFC75F1B701C8600AD841F /* ASInsetLayoutSpec.h in Headers */, + CCEDDDCA200C2AC300FFCD0A /* ASConfigurationInternal.h in Headers */, 34EFC7671B701CD900AD841F /* ASLayout.h in Headers */, DBDB83951C6E879900D0098C /* ASPagerFlowLayout.h in Headers */, 34EFC7691B701CE100AD841F /* ASLayoutElement.h in Headers */, @@ -1942,6 +1983,7 @@ 34EFC76E1B701CF400AD841F /* ASRatioLayoutSpec.h in Headers */, DB55C2671C641AE4004EDCF5 /* ASContextTransitioning.h in Headers */, CCA282C41E9EAE630037E8B7 /* ASLayerBackingTipProvider.h in Headers */, + CCEDDDCF200C42A200FFCD0A /* ASConfigurationDelegate.h in Headers */, E5C347B31ECB40AA00EC4BE4 /* ASTableNode+Beta.h in Headers */, 6900C5F41E8072DA00BCD75C /* ASImageNode+Private.h in Headers */, 68B0277B1C1A79D60041016B /* ASDisplayNode+Beta.h in Headers */, @@ -1953,6 +1995,7 @@ CCCCCCDB1EC3EF060087FE10 /* ASTextLine.h in Headers */, 9C70F20E1CDBE9E5007D6C76 /* NSArray+Diffing.h in Headers */, CCCCCCE71EC3F0FC0087FE10 /* NSAttributedString+ASText.h in Headers */, + CC7AF196200D9BD500A21BDE /* ASExperimentalFeatures.h in Headers */, CCCCCCDF1EC3EF060087FE10 /* ASTextRunDelegate.h in Headers */, 9C49C3701B853961000B0DD5 /* ASStackLayoutElement.h in Headers */, 34EFC7701B701CFA00AD841F /* ASStackLayoutDefines.h in Headers */, @@ -2105,6 +2148,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + CC224E962066CA6D00BBA57F /* configuration.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2175,6 +2219,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CCEDDDD9200C518800FFCD0A /* ASConfigurationTests.m in Sources */, E51B78BF1F028ABF00E32604 /* ASLayoutFlatteningTests.m in Sources */, 4496D0731FA9EA6B001CC8D5 /* ASTraitCollectionTests.m in Sources */, 29CDC2E21AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m in Sources */, @@ -2315,6 +2360,7 @@ B35062181B010EFD0018CF92 /* ASDataController.mm in Sources */, CCB1F95A1EFB60A5009C7475 /* ASLog.m in Sources */, 767E7F8E1C90191D0066C000 /* AsyncDisplayKit+Debug.m in Sources */, + CCEDDDCB200C2AC300FFCD0A /* ASConfigurationInternal.m in Sources */, CCCCCCD61EC3EF060087FE10 /* ASTextDebugOption.m in Sources */, 34EFC75C1B701BD200AD841F /* ASDimension.mm in Sources */, B350624E1B010EFD0018CF92 /* ASDisplayNode+AsyncDisplay.mm in Sources */, @@ -2353,6 +2399,7 @@ DECBD6EA1BE56E1900CF4905 /* ASButtonNode.mm in Sources */, CCCCCCE01EC3EF060087FE10 /* ASTextRunDelegate.m in Sources */, CCCCCCDA1EC3EF060087FE10 /* ASTextLayout.m in Sources */, + CCEDDDD1200C488000FFCD0A /* ASConfiguration.m in Sources */, 254C6B841BF94F8A003EC431 /* ASTextNodeWordKerner.m in Sources */, E5E2D7301EA780DF005C24C6 /* ASCollectionGalleryLayoutDelegate.mm in Sources */, 34EFC76B1B701CEB00AD841F /* ASLayoutSpec.mm in Sources */, @@ -2383,6 +2430,7 @@ E54E81FD1EB357BD00FFE8E1 /* ASPageTable.m in Sources */, 34EFC7721B701D0300AD841F /* ASStackLayoutSpec.mm in Sources */, 7AB338661C55B3420055FDE8 /* ASRelativeLayoutSpec.mm in Sources */, + CC7AF198200DAB2200A21BDE /* ASExperimentalFeatures.m in Sources */, E5B2252E1F17E521001E1431 /* ASDispatch.m in Sources */, 696F01EE1DD2AF450049FBD5 /* ASEventLog.mm in Sources */, 9C70F2051CDA4F06007D6C76 /* ASTraitCollection.m in Sources */, diff --git a/CHANGELOG.md b/CHANGELOG.md index 79227ca03..22c737c17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - [ASCollectionNode] Added support for interactive item movement. [Adlai Holler](https://github.com/Adlai-Holler) - Added an experimental "no-copy" rendering API. See ASGraphicsContext.h for info. [Adlai Holler](https://github.com/Adlai-Holler) - Dropped support for iOS 8. [Adlai Holler](https://github.com/Adlai-Holler) +- Added a configuration API – a unified place to turn on/off experimental Texture features. See `ASConfiguration.h` for info. [Adlai Holler](https://github.com/Adlai-Holler) - **Breaking** Changes to ASNetworkImageNode: [Adlai Holler](https://github.com/Adlai-Holler) - Modified `ASImageDownloaderCompletion` to add an optional `id userInfo` field. Your custom downloader can pass `nil`. - Modified the last argument to `-[ASNetworkImageNodeDelegate imageNode:didLoadImage:info:]` method from a struct to an object of new class `ASNetworkImageLoadInfo` which includes other metadata about the load operation. diff --git a/Schemas/configuration.json b/Schemas/configuration.json new file mode 100644 index 000000000..366b29233 --- /dev/null +++ b/Schemas/configuration.json @@ -0,0 +1,23 @@ +{ + "id": "configuration.json", + "title": "configuration", + "description" : "Schema definition of a Texture Configuration", + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "version" : { + "type" : "number" + }, + "experimental_features": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "exp_graphics_contexts", + "exp_text_node", + "exp_interface_state_coalesce" + ] + } + } + } +} diff --git a/Source/ASConfiguration.h b/Source/ASConfiguration.h new file mode 100644 index 000000000..b024bb7e9 --- /dev/null +++ b/Source/ASConfiguration.h @@ -0,0 +1,62 @@ +// +// ASConfiguration.h +// Texture +// +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@protocol ASConfigurationDelegate; + +NS_ASSUME_NONNULL_BEGIN + +static NSInteger const ASConfigurationSchemaCurrentVersion = 1; + +AS_SUBCLASSING_RESTRICTED +@interface ASConfiguration : NSObject + +/** + * Initialize this configuration with the provided dictionary, + * or nil to create an empty configuration. + * + * The schema is located in `schemas/configuration.json`. + */ +- (instancetype)initWithDictionary:(nullable NSDictionary *)dictionary; + +/** + * The delegate for configuration-related events. + * Delegate methods are called from a serial queue. + */ +@property (nonatomic, strong, nullable) id delegate; + +/** + * The experimental features to enable in Texture. + * See ASExperimentalFeatures for functions to convert to/from a string array. + */ +@property (nonatomic) ASExperimentalFeatures experimentalFeatures; + +@end + +/** + * Implement this method in a category to make your + * configuration available to Texture. It will be read + * only once and copied. + * + * NOTE: To specify your configuration at compile-time, you can + * define AS_FIXED_CONFIG_JSON as a C-string of JSON. This method + * will then be implemented to parse that string and generate + * a configuration. + */ +@interface ASConfiguration (UserProvided) ++ (ASConfiguration *)textureConfiguration NS_RETURNS_RETAINED; +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/ASConfiguration.m b/Source/ASConfiguration.m new file mode 100644 index 000000000..93c8c8fb0 --- /dev/null +++ b/Source/ASConfiguration.m @@ -0,0 +1,67 @@ +// +// ASConfiguration.m +// Texture +// +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +/// Not too performance-sensitive here. + +/// Get this from C++, without the extra exception handling. +#define autotype __auto_type + +@implementation ASConfiguration + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary +{ + if (self = [super init]) { + autotype featureStrings = ASDynamicCast(dictionary[@"experimental_features"], NSArray); + autotype version = ASDynamicCast(dictionary[@"version"], NSNumber).integerValue; + if (version != ASConfigurationSchemaCurrentVersion) { + NSLog(@"Texture warning: configuration schema is old version (%zd vs %zd)", version, ASConfigurationSchemaCurrentVersion); + } + self.experimentalFeatures = ASExperimentalFeaturesFromArray(featureStrings); + } + return self; +} + +- (id)copyWithZone:(NSZone *)zone +{ + ASConfiguration *config = [[ASConfiguration alloc] initWithDictionary:nil]; + config.experimentalFeatures = self.experimentalFeatures; + config.delegate = self.delegate; + return config; +} + +@end + +//#define AS_FIXED_CONFIG_JSON "{ \"version\" : 1, \"experimental_features\": [ \"exp_text_node\" ] }" + +#ifdef AS_FIXED_CONFIG_JSON + +@implementation ASConfiguration (UserProvided) + ++ (ASConfiguration *)textureConfiguration NS_RETURNS_RETAINED +{ + NSData *data = [@AS_FIXED_CONFIG_JSON dataUsingEncoding:NSUTF8StringEncoding]; + NSError *error; + NSDictionary *d = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; + if (!d) { + NSAssert(NO, @"Error parsing fixed config string '%s': %@", AS_FIXED_CONFIG_JSON, error); + return nil; + } else { + return [[ASConfiguration alloc] initWithDictionary:d]; + } +} + +@end + +#endif // AS_FIXED_CONFIG_JSON diff --git a/Source/ASConfigurationDelegate.h b/Source/ASConfigurationDelegate.h new file mode 100644 index 000000000..88055f037 --- /dev/null +++ b/Source/ASConfigurationDelegate.h @@ -0,0 +1,31 @@ +// +// ASConfigurationDelegate.h +// Texture +// +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Used to communicate configuration-related events to the client. + */ +@protocol ASConfigurationDelegate + +/** + * Texture performed its first behavior related to the feature(s). + * This can be useful for tracking the impact of the behavior (A/B testing). + */ +- (void)textureDidActivateExperimentalFeatures:(ASExperimentalFeatures)features; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 98f3f1b80..72eaf0331 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -2915,7 +2915,7 @@ - (void)didExitHierarchy } }; - if ([[ASCATransactionQueue sharedQueue] disabled]) { + if (!ASCATransactionQueue.sharedQueue.enabled) { dispatch_async(dispatch_get_main_queue(), exitVisibleInterfaceState); } else { exitVisibleInterfaceState(); @@ -2980,7 +2980,7 @@ - (ASInterfaceState)interfaceState - (void)setInterfaceState:(ASInterfaceState)newState { - if ([[ASCATransactionQueue sharedQueue] disabled]) { + if (!ASCATransactionQueue.sharedQueue.enabled) { [self applyPendingInterfaceState:newState]; } else { ASDN::MutexLocker l(__instanceLock__); @@ -3012,7 +3012,7 @@ - (void)applyPendingInterfaceState:(ASInterfaceState)newPendingState ASDN::MutexLocker l(__instanceLock__); // newPendingState will not be used when ASCATransactionQueue is enabled // and use _pendingInterfaceState instead for interfaceState update. - if ([[ASCATransactionQueue sharedQueue] disabled]) { + if (!ASCATransactionQueue.sharedQueue.enabled) { _pendingInterfaceState = newPendingState; } oldState = _interfaceState; diff --git a/Source/ASExperimentalFeatures.h b/Source/ASExperimentalFeatures.h new file mode 100644 index 000000000..9cb3d419e --- /dev/null +++ b/Source/ASExperimentalFeatures.h @@ -0,0 +1,36 @@ +// +// ASExperimentalFeatures.h +// Texture +// +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN +ASDISPLAYNODE_EXTERN_C_BEGIN + +/** + * A bit mask of features. + */ +typedef NS_OPTIONS(NSUInteger, ASExperimentalFeatures) { + ASExperimentalGraphicsContexts = 1 << 0, // exp_graphics_contexts + ASExperimentalTextNode = 1 << 1, // exp_text_node + ASExperimentalInterfaceStateCoalescing = 1 << 2, // exp_interface_state_coalesce + ASExperimentalFeatureAll = 0xFFFFFFFF +}; + +/// Convert flags -> name array. +NSArray *ASExperimentalFeaturesGetNames(ASExperimentalFeatures flags); + +/// Convert name array -> flags. +ASExperimentalFeatures ASExperimentalFeaturesFromArray(NSArray *array); + +ASDISPLAYNODE_EXTERN_C_END +NS_ASSUME_NONNULL_END diff --git a/Source/ASExperimentalFeatures.m b/Source/ASExperimentalFeatures.m new file mode 100644 index 000000000..5ce20c70b --- /dev/null +++ b/Source/ASExperimentalFeatures.m @@ -0,0 +1,45 @@ +// +// ASExperimentalFeatures.m +// Texture +// +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NSArray *ASExperimentalFeaturesGetNames(ASExperimentalFeatures flags) +{ + NSArray *allNames = ASCreateOnce((@[@"exp_graphics_contexts", + @"exp_text_node", + @"exp_interface_state_coalesce"])); + + if (flags == ASExperimentalFeatureAll) { + return allNames; + } + + // Go through all names, testing each bit. + NSUInteger i = 0; + return ASArrayByFlatMapping(allNames, NSString *name, ({ + (flags & (1 << i++)) ? name : nil; + })); +} + +// O(N^2) but with counts this small, it's probably faster +// than hashing the strings. +ASExperimentalFeatures ASExperimentalFeaturesFromArray(NSArray *array) +{ + NSArray *allNames = ASExperimentalFeaturesGetNames(ASExperimentalFeatureAll); + ASExperimentalFeatures result = 0; + for (NSString *str in array) { + NSUInteger i = [allNames indexOfObject:str]; + if (i != NSNotFound) { + result |= (1 << i); + } + } + return result; +} diff --git a/Source/ASNetworkImageLoadInfo.h b/Source/ASNetworkImageLoadInfo.h index 51e23e375..6db366a9c 100644 --- a/Source/ASNetworkImageLoadInfo.h +++ b/Source/ASNetworkImageLoadInfo.h @@ -1,9 +1,13 @@ // // ASNetworkImageLoadInfo.h -// AsyncDisplayKit +// Texture // -// Created by Adlai on 1/30/18. -// Copyright © 2018 Facebook. All rights reserved. +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/ASNetworkImageLoadInfo.m b/Source/ASNetworkImageLoadInfo.m index 69dfee760..00baa1bf7 100644 --- a/Source/ASNetworkImageLoadInfo.m +++ b/Source/ASNetworkImageLoadInfo.m @@ -1,9 +1,13 @@ // // ASNetworkImageLoadInfo.m -// AsyncDisplayKit +// Texture // -// Created by Adlai on 1/30/18. -// Copyright © 2018 Facebook. All rights reserved. +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Source/ASRunLoopQueue.h b/Source/ASRunLoopQueue.h index 266cd71f3..291cc4abf 100644 --- a/Source/ASRunLoopQueue.h +++ b/Source/ASRunLoopQueue.h @@ -59,7 +59,9 @@ AS_SUBCLASSING_RESTRICTED @interface ASCATransactionQueue : ASAbstractRunLoopQueue @property (atomic, readonly) BOOL isEmpty; -@property (atomic, readonly) BOOL disabled; + +@property (atomic, readonly, getter=isEnabled) BOOL enabled; + /** * The queue to run on main run loop before CATransaction commit. * @@ -72,11 +74,6 @@ AS_SUBCLASSING_RESTRICTED - (void)enqueue:(id)object; -/** - * @abstract Apply a node's interfaceState immediately rather than adding to the queue. - */ -- (void)disable; - @end diff --git a/Source/ASRunLoopQueue.mm b/Source/ASRunLoopQueue.mm index 782618cc3..d30a15a39 100644 --- a/Source/ASRunLoopQueue.mm +++ b/Source/ASRunLoopQueue.mm @@ -16,6 +16,7 @@ // #import +#import #import #import #import @@ -494,7 +495,6 @@ @interface ASCATransactionQueue () { CFRunLoopObserverRef _postTransactionObserver; NSPointerArray *_internalQueue; ASDN::RecursiveMutex _internalQueueLock; - BOOL _disableInterfaceStateCoalesce; BOOL _CATransactionCommitInProgress; // In order to not pollute the top-level activities, each queue has 1 root activity. @@ -671,7 +671,7 @@ - (void)enqueue:(id)object return; } - if (_disableInterfaceStateCoalesce || _CATransactionCommitInProgress) { + if (!self.enabled || _CATransactionCommitInProgress) { [object prepareForCATransactionCommit]; return; } @@ -702,14 +702,9 @@ - (BOOL)isEmpty return _internalQueue.count == 0; } -- (void)disable +- (BOOL)isEnabled { - _disableInterfaceStateCoalesce = YES; -} - -- (BOOL)disabled -{ - return _disableInterfaceStateCoalesce; + return ASActivateExperimentalFeature(ASExperimentalInterfaceStateCoalescing); } @end diff --git a/Source/ASTextNode+Beta.h b/Source/ASTextNode+Beta.h index 8c7556811..d60e0c13c 100644 --- a/Source/ASTextNode+Beta.h +++ b/Source/ASTextNode+Beta.h @@ -21,20 +21,6 @@ NS_ASSUME_NONNULL_BEGIN -// When enabled, use ASTextNode2 for subclasses, random instances, or all instances of ASTextNode. -// See ASAvailability.h declaration of ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE for a compile-time option. -typedef NS_OPTIONS(NSUInteger, ASTextNodeExperimentOptions) { - // All subclass instances use the experimental implementation. - ASTextNodeExperimentSubclasses = 1 << 0, - // Random instances of ASTextNode (50% chance) (not subclasses) use experimental impl. - // Useful for profiling with apps that have no custom text node subclasses. - ASTextNodeExperimentRandomInstances = 1 << 1, - // All instances of ASTextNode itself use experimental implementation. Supersedes `.randomInstances`. - ASTextNodeExperimentAllInstances = 1 << 2, - // Add highlighting etc. for debugging. - ASTextNodeExperimentDebugging = 1 << 3 -}; - @interface ASTextNode () /** @@ -52,14 +38,6 @@ typedef NS_OPTIONS(NSUInteger, ASTextNodeExperimentOptions) { */ @property (nonatomic, assign) UIEdgeInsets textContainerInset; -/** - * Opt in to an experimental implementation of text node. The implementation may improve performance and correctness, - * but may not support all features and has not been thoroughly tested in production. - * - * @precondition You may not call this after allocating any text nodes. You may only call this once. - */ -+ (void)setExperimentOptions:(ASTextNodeExperimentOptions)options; - /** * Returns YES if this node is using the experimental implementation. NO otherwise. Will not change. */ diff --git a/Source/ASTextNode.h b/Source/ASTextNode.h index f3670bb0f..269ca8f81 100644 --- a/Source/ASTextNode.h +++ b/Source/ASTextNode.h @@ -17,31 +17,10 @@ #import #import -#if ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE - #import -#endif +#import NS_ASSUME_NONNULL_BEGIN -@protocol ASTextNodeDelegate; - -#if !ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE - -/** - * Highlight styles. - */ -typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { - /** - * Highlight style for text on a light background. - */ - ASTextNodeHighlightStyleLight, - - /** - * Highlight style for text on a dark background. - */ - ASTextNodeHighlightStyleDark -}; - /** @abstract Draws interactive rich text. @discussion Backed by TextKit. @@ -238,70 +217,6 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { @end -#else - -@interface ASTextNode : ASTextNode2 -@end - -#endif - -/** - * @abstract Text node delegate. - */ -@protocol ASTextNodeDelegate -@optional - -/** - @abstract Indicates to the delegate that a link was tapped within a text node. - @param textNode The ASTextNode containing the link that was tapped. - @param attribute The attribute that was tapped. Will not be nil. - @param value The value of the tapped attribute. - @param point The point within textNode, in textNode's coordinate system, that was tapped. - @param textRange The range of highlighted text. - */ -- (void)textNode:(ASTextNode *)textNode tappedLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point textRange:(NSRange)textRange; - -/** - @abstract Indicates to the delegate that a link was tapped within a text node. - @param textNode The ASTextNode containing the link that was tapped. - @param attribute The attribute that was tapped. Will not be nil. - @param value The value of the tapped attribute. - @param point The point within textNode, in textNode's coordinate system, that was tapped. - @param textRange The range of highlighted text. - @discussion In addition to implementing this method, the delegate must be set on the text - node before it is loaded (the recognizer is created in -didLoad) - */ -- (void)textNode:(ASTextNode *)textNode longPressedLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point textRange:(NSRange)textRange; - -//! @abstract Called when the text node's truncation string has been tapped. -- (void)textNodeTappedTruncationToken:(ASTextNode *)textNode; - -/** - @abstract Indicates to the text node if an attribute should be considered a link. - @param textNode The text node containing the entity attribute. - @param attribute The attribute that was tapped. Will not be nil. - @param value The value of the tapped attribute. - @param point The point within textNode, in textNode's coordinate system, that was touched to trigger a highlight. - @discussion If not implemented, the default value is YES. - @return YES if the entity attribute should be a link, NO otherwise. - */ -- (BOOL)textNode:(ASTextNode *)textNode shouldHighlightLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point; - -/** - @abstract Indicates to the text node if an attribute is a valid long-press target - @param textNode The text node containing the entity attribute. - @param attribute The attribute that was tapped. Will not be nil. - @param value The value of the tapped attribute. - @param point The point within textNode, in textNode's coordinate system, that was long-pressed. - @discussion If not implemented, the default value is NO. - @return YES if the entity attribute should be treated as a long-press target, NO otherwise. - */ -- (BOOL)textNode:(ASTextNode *)textNode shouldLongPressLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point; - -@end - -#if !ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE - @interface ASTextNode (Unavailable) - (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock __unavailable; @@ -334,6 +249,4 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { @end -#endif - NS_ASSUME_NONNULL_END diff --git a/Source/ASTextNode.mm b/Source/ASTextNode.mm index b57d7ccea..e98dfb261 100644 --- a/Source/ASTextNode.mm +++ b/Source/ASTextNode.mm @@ -18,7 +18,6 @@ #import #import -#if !ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE #import #import @@ -27,6 +26,7 @@ #import #import #import +#import #import #import #import @@ -1309,69 +1309,35 @@ + (void)_registerAttributedText:(NSAttributedString *)str } #endif -// Allocate _experimentLock on the heap to prevent destruction at app exit (https://github.com/TextureGroup/Texture/issues/136) -static ASDN::StaticMutex& _experimentLock = *new ASDN::StaticMutex; -static ASTextNodeExperimentOptions _experimentOptions; -static BOOL _hasAllocatedNode; - -+ (void)setExperimentOptions:(ASTextNodeExperimentOptions)options ++ (id)allocWithZone:(struct _NSZone *)zone { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - ASDN::StaticMutexLocker lock(_experimentLock); - - // They must call this before allocating any text nodes. - ASDisplayNodeAssertFalse(_hasAllocatedNode); - - _experimentOptions = options; - - // Set superclass of all subclasses to ASTextNode2 - if (options & ASTextNodeExperimentSubclasses) { - unsigned int classCount; - Class originalClass = [ASTextNode class]; - Class newClass = [ASTextNode2 class]; - Class *classes = objc_copyClassList(&classCount); - for (int i = 0; i < classCount; i++) { - Class c = classes[i]; - if (class_getSuperclass(c) == originalClass) { + // If they're not experimenting, just forward. + if (!ASActivateExperimentalFeature(ASExperimentalTextNode)) { + return [super allocWithZone:zone]; + } + + // We are plain ASTextNode. Just swap in an ASTextNode2 instead. + if (self == [ASTextNode class]) { + return (ASTextNode *)[ASTextNode2 allocWithZone:zone]; + } + + // We are descended from ASTextNode. We need to change the superclass for the + // ASTextNode subclass to ASTextNode2. + // Walk up the class hierarchy until we find ASTextNode. + Class s; + for (Class c = self; c != [ASTextNode class]; c = s) { + s = class_getSuperclass(c); + if (s == [ASTextNode class]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - class_setSuperclass(c, newClass); + // Direct descendent. Update superclass of c and end. + class_setSuperclass(c, [ASTextNode2 class]); #pragma clang diagnostic pop - } - } - free(classes); + break; } - - if (options & ASTextNodeExperimentDebugging) { - [ASTextNode2 enableDebugging]; - } - }); -} - -+ (id)allocWithZone:(struct _NSZone *)zone -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - ASDN::StaticMutexLocker lock(_experimentLock); - _hasAllocatedNode = YES; - }); - - // All instances || (random instances && rand() != 0) - BOOL useExperiment = (_experimentOptions & ASTextNodeExperimentAllInstances) - || ((_experimentOptions & ASTextNodeExperimentRandomInstances) - && (arc4random_uniform(2) != 0)); - - if (useExperiment) { - return (ASTextNode *)[ASTextNode2 allocWithZone:zone]; - } else { - return [super allocWithZone:zone]; } -} -- (BOOL)usingExperiment -{ - return NO; + return [super allocWithZone:zone]; } @end @@ -1399,10 +1365,3 @@ - (NSAttributedString *)truncationAttributedString } @end - -#else - -@implementation ASTextNode -@end - -#endif diff --git a/Source/ASTextNode2.h b/Source/ASTextNode2.h index aef3ac468..9eecbf546 100644 --- a/Source/ASTextNode2.h +++ b/Source/ASTextNode2.h @@ -11,28 +11,7 @@ // #import - -#if !ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE -// Import this to get ASTextNodeHighlightStyle -#import -#else -@protocol ASTextNodeDelegate; - -/** - * Highlight styles. - */ -typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { - /** - * Highlight style for text on a light background. - */ - ASTextNodeHighlightStyleLight, - - /** - * Highlight style for text on a dark background. - */ - ASTextNodeHighlightStyleDark -}; -#endif +#import NS_ASSUME_NONNULL_BEGIN diff --git a/Source/ASTextNodeCommon.h b/Source/ASTextNodeCommon.h new file mode 100644 index 000000000..0ff51b402 --- /dev/null +++ b/Source/ASTextNodeCommon.h @@ -0,0 +1,86 @@ +// +// ASTextNodeCommon.h +// Texture +// +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@class ASTextNode; + +/** + * Highlight styles. + */ +typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { + /** + * Highlight style for text on a light background. + */ + ASTextNodeHighlightStyleLight, + + /** + * Highlight style for text on a dark background. + */ + ASTextNodeHighlightStyleDark +}; + +/** + * @abstract Text node delegate. + */ +@protocol ASTextNodeDelegate +@optional + +/** + @abstract Indicates to the delegate that a link was tapped within a text node. + @param textNode The ASTextNode containing the link that was tapped. + @param attribute The attribute that was tapped. Will not be nil. + @param value The value of the tapped attribute. + @param point The point within textNode, in textNode's coordinate system, that was tapped. + @param textRange The range of highlighted text. + */ +- (void)textNode:(ASTextNode *)textNode tappedLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point textRange:(NSRange)textRange; + +/** + @abstract Indicates to the delegate that a link was tapped within a text node. + @param textNode The ASTextNode containing the link that was tapped. + @param attribute The attribute that was tapped. Will not be nil. + @param value The value of the tapped attribute. + @param point The point within textNode, in textNode's coordinate system, that was tapped. + @param textRange The range of highlighted text. + @discussion In addition to implementing this method, the delegate must be set on the text + node before it is loaded (the recognizer is created in -didLoad) + */ +- (void)textNode:(ASTextNode *)textNode longPressedLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point textRange:(NSRange)textRange; + +//! @abstract Called when the text node's truncation string has been tapped. +- (void)textNodeTappedTruncationToken:(ASTextNode *)textNode; + +/** + @abstract Indicates to the text node if an attribute should be considered a link. + @param textNode The text node containing the entity attribute. + @param attribute The attribute that was tapped. Will not be nil. + @param value The value of the tapped attribute. + @param point The point within textNode, in textNode's coordinate system, that was touched to trigger a highlight. + @discussion If not implemented, the default value is YES. + @return YES if the entity attribute should be a link, NO otherwise. + */ +- (BOOL)textNode:(ASTextNode *)textNode shouldHighlightLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point; + +/** + @abstract Indicates to the text node if an attribute is a valid long-press target + @param textNode The text node containing the entity attribute. + @param attribute The attribute that was tapped. Will not be nil. + @param value The value of the tapped attribute. + @param point The point within textNode, in textNode's coordinate system, that was long-pressed. + @discussion If not implemented, the default value is NO. + @return YES if the entity attribute should be treated as a long-press target, NO otherwise. + */ +- (BOOL)textNode:(ASTextNode *)textNode shouldLongPressLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point; + +@end + diff --git a/Source/AsyncDisplayKit.h b/Source/AsyncDisplayKit.h index 67e1720b6..111a8f310 100644 --- a/Source/AsyncDisplayKit.h +++ b/Source/AsyncDisplayKit.h @@ -21,6 +21,8 @@ #import #import #import +#import +#import #import #import diff --git a/Source/Base/ASAvailability.h b/Source/Base/ASAvailability.h index f400dea3c..c05ddd3fa 100644 --- a/Source/Base/ASAvailability.h +++ b/Source/Base/ASAvailability.h @@ -56,11 +56,8 @@ #define YOGA __has_include(YOGA_HEADER_PATH) #endif -// When enabled, use ASTextNode2 for ALL instances of ASTextNode. -// This includes what ASButtonNode uses internally, as well as all app references to ASTextNode. -// See ASTextNode+Beta.h declaration of ASTextNodeExperimentOptions for more details. -#ifndef ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE - #define ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE 0 +#ifdef ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE + #error "ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE is unavailable. See ASConfiguration.h." #endif #define AS_PIN_REMOTE_IMAGE __has_include() diff --git a/Source/Details/ASElementMap.h b/Source/Details/ASElementMap.h index eed7eff26..a73f80a92 100644 --- a/Source/Details/ASElementMap.h +++ b/Source/Details/ASElementMap.h @@ -31,6 +31,11 @@ NS_ASSUME_NONNULL_BEGIN AS_SUBCLASSING_RESTRICTED @interface ASElementMap : NSObject +/** + * The total number of elements in this map. + */ +@property (readonly) NSUInteger count; + /** * The number of sections (of items) in this map. */ diff --git a/Source/Details/ASElementMap.m b/Source/Details/ASElementMap.m index 08e90d401..f5646de78 100644 --- a/Source/Details/ASElementMap.m +++ b/Source/Details/ASElementMap.m @@ -75,6 +75,11 @@ - (instancetype)initWithSections:(NSArray *)sections items:(ASColle return self; } +- (NSUInteger)count +{ + return _elementToIndexPathMap.count; +} + - (NSArray *)itemIndexPaths { return ASIndexPathsForTwoDimensionalArray(_sectionsOfItems); diff --git a/Source/Details/ASGraphicsContext.h b/Source/Details/ASGraphicsContext.h index b7f808c97..0713f104f 100644 --- a/Source/Details/ASGraphicsContext.h +++ b/Source/Details/ASGraphicsContext.h @@ -31,15 +31,6 @@ NS_ASSUME_NONNULL_BEGIN ASDISPLAYNODE_EXTERN_C_BEGIN -/** - * Call this to enable the experimental no-copy rendering. - * - * Returns YES if it was enabled, or NO + assert if it's too late because - * rendering has already started. In practice it's fine to call this - * during -didFinishLaunchingWithOptions:. - */ -extern BOOL ASEnableNoCopyRendering(void); - /** * Creates a one-shot context. * diff --git a/Source/Details/ASGraphicsContext.m b/Source/Details/ASGraphicsContext.m index 70294d10a..37810a106 100644 --- a/Source/Details/ASGraphicsContext.m +++ b/Source/Details/ASGraphicsContext.m @@ -13,36 +13,12 @@ #import "ASGraphicsContext.h" #import #import +#import #import #import #import -#import #import -#pragma mark - Feature Gating - -// Two flags that we atomically manipulate to control the feature. -typedef NS_OPTIONS(uint, ASNoCopyFlags) { - ASNoCopyEnabled = 1 << 0, - ASNoCopyBlocked = 1 << 1 -}; -static atomic_uint __noCopyFlags; - -// Check if it's blocked, and set the enabled flag if not. -extern BOOL ASEnableNoCopyRendering() -{ - ASNoCopyFlags expectedFlags = 0; - BOOL enabled = atomic_compare_exchange_strong(&__noCopyFlags, &expectedFlags, ASNoCopyEnabled); - ASDisplayNodeCAssert(enabled, @"Can't enable no-copy rendering after first render started."); - return enabled; -} - -// Check if it's enabled and set the "blocked" flag either way. -static BOOL ASNoCopyRenderingBlockAndCheckEnabled() { - ASNoCopyFlags oldFlags = atomic_fetch_or(&__noCopyFlags, ASNoCopyBlocked); - return (oldFlags & ASNoCopyEnabled) != 0; -} - /** * Our version of the private CGBitmapGetAlignedBytesPerRow function. * @@ -67,7 +43,7 @@ static size_t ASGraphicsGetAlignedBytesPerRow(size_t baseValue) { extern void ASGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale) { - if (!ASNoCopyRenderingBlockAndCheckEnabled()) { + if (!ASActivateExperimentalFeature(ASExperimentalGraphicsContexts)) { UIGraphicsBeginImageContextWithOptions(size, opaque, scale); return; } @@ -132,7 +108,7 @@ extern void ASGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGF extern UIImage * _Nullable ASGraphicsGetImageAndEndCurrentContext() NS_RETURNS_RETAINED { - if (!ASNoCopyRenderingBlockAndCheckEnabled()) { + if (!ASActivateExperimentalFeature(ASExperimentalGraphicsContexts)) { UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; @@ -186,7 +162,7 @@ extern void ASGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGF extern void ASGraphicsEndImageContext() { - if (!ASNoCopyRenderingBlockAndCheckEnabled()) { + if (!ASActivateExperimentalFeature(ASExperimentalGraphicsContexts)) { UIGraphicsEndImageContext(); return; } diff --git a/Source/Private/ASConfigurationInternal.h b/Source/Private/ASConfigurationInternal.h new file mode 100644 index 000000000..f93af4bbc --- /dev/null +++ b/Source/Private/ASConfigurationInternal.h @@ -0,0 +1,38 @@ +// +// ASConfigurationInternal.h +// Texture +// +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN +ASDISPLAYNODE_EXTERN_C_BEGIN + +/** + * Quickly check if an experiment is enabled and notify the delegate + * that it's been activated. + * + * The delegate will be notified asynchronously. + */ +BOOL ASActivateExperimentalFeature(ASExperimentalFeatures option); + +AS_SUBCLASSING_RESTRICTED +@interface ASConfigurationManager : NSObject + +/** + * No API for now. + * Just use ASActivateExperimentalFeature to access this efficiently. + */ + +@end + +NS_ASSUME_NONNULL_END +ASDISPLAYNODE_EXTERN_C_END diff --git a/Source/Private/ASConfigurationInternal.m b/Source/Private/ASConfigurationInternal.m new file mode 100644 index 000000000..e9f0705ec --- /dev/null +++ b/Source/Private/ASConfigurationInternal.m @@ -0,0 +1,96 @@ +// +// ASConfigurationInternal.m +// Texture +// +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASConfigurationInternal.h" +#import +#import +#import + +#define ASGetSharedConfigMgr() (__bridge ASConfigurationManager *)ASConfigurationManager.sharedInstance + +@implementation ASConfigurationManager { + ASConfiguration *_config; + dispatch_queue_t _delegateQueue; + _Atomic(ASExperimentalFeatures) _activatedExperiments; +} + +/// Return CFTypeRef to avoid retain/release on this singleton. ++ (CFTypeRef)sharedInstance +{ + static CFTypeRef inst; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + inst = (__bridge_retained CFTypeRef)[[ASConfigurationManager alloc] init]; + }); + return inst; +} + ++ (ASConfiguration *)defaultConfiguration NS_RETURNS_RETAINED +{ + ASConfiguration *config = [[ASConfiguration alloc] init]; + // On by default for now, pending fix for https://github.com/TextureGroup/Texture/issues/853 + config.experimentalFeatures = ASExperimentalInterfaceStateCoalescing; + return config; +} + +- (instancetype)init +{ + if (self = [super init]) { + _delegateQueue = dispatch_queue_create("org.TextureGroup.Texture.ConfigNotifyQueue", DISPATCH_QUEUE_SERIAL); + if ([ASConfiguration respondsToSelector:@selector(textureConfiguration)]) { + _config = [[ASConfiguration textureConfiguration] copy]; + } else { + _config = [ASConfigurationManager defaultConfiguration]; + } + } + return self; +} + +- (BOOL)activateExperimentalFeature:(ASExperimentalFeatures)requested +{ + if (_config == nil) { + return NO; + } + + NSAssert(__builtin_popcount(requested) == 1, @"Cannot activate multiple features at once with this method."); + + // If they're disabled, ignore them. + ASExperimentalFeatures enabled = requested & _config.experimentalFeatures; + ASExperimentalFeatures prevTriggered = atomic_fetch_or(&_activatedExperiments, enabled); + ASExperimentalFeatures newlyTriggered = enabled & ~prevTriggered; + + // Notify delegate if needed. + if (newlyTriggered != 0) { + __unsafe_unretained id del = _config.delegate; + dispatch_async(_delegateQueue, ^{ + [del textureDidActivateExperimentalFeatures:newlyTriggered]; + }); + } + + return (enabled != 0); +} + +#if DEBUG ++ (void)test_resetWithConfiguration:(ASConfiguration *)configuration +{ + ASConfigurationManager *inst = ASGetSharedConfigMgr(); + inst->_config = configuration ?: [self defaultConfiguration]; + atomic_store(&inst->_activatedExperiments, 0); +} +#endif + +@end + +BOOL ASActivateExperimentalFeature(ASExperimentalFeatures feature) +{ + return [ASGetSharedConfigMgr() activateExperimentalFeature:feature]; +} diff --git a/Source/Private/ASNetworkImageLoadInfo+Private.h b/Source/Private/ASNetworkImageLoadInfo+Private.h index db0435231..a77d48af6 100644 --- a/Source/Private/ASNetworkImageLoadInfo+Private.h +++ b/Source/Private/ASNetworkImageLoadInfo+Private.h @@ -1,9 +1,13 @@ // // ASNetworkImageLoadInfo+Private.h -// AsyncDisplayKit +// Texture // -// Created by Adlai on 1/30/18. -// Copyright © 2018 Facebook. All rights reserved. +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASCollectionViewTests.mm b/Tests/ASCollectionViewTests.mm index c3dc8a2c4..9ea6fe557 100644 --- a/Tests/ASCollectionViewTests.mm +++ b/Tests/ASCollectionViewTests.mm @@ -862,7 +862,7 @@ - (void)testThatDeletedItemsAreMarkedInvisible [cn waitUntilAllUpdatesAreProcessed]; [cn.view layoutIfNeeded]; ASCellNode *node = [cn nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; - ASCATransactionQueueWait(); + ASCATransactionQueueWait(nil); XCTAssertTrue(node.visible); testController.asyncDelegate->_itemCounts = {0}; [cn deleteItemsAtIndexPaths: @[[NSIndexPath indexPathForItem:0 inSection:0]]]; @@ -1074,7 +1074,7 @@ - (void)testInitialRangeBounds for (NSInteger i = 0; i < c; i++) { NSIndexPath *ip = [NSIndexPath indexPathForItem:i inSection:s]; ASCellNode *node = [cn nodeForItemAtIndexPath:ip]; - ASCATransactionQueueWait(); + ASCATransactionQueueWait(nil); if (node.inPreloadState) { CGRect frame = [cn.view layoutAttributesForItemAtIndexPath:ip].frame; r = CGRectUnion(r, frame); diff --git a/Tests/ASConfigurationTests.m b/Tests/ASConfigurationTests.m new file mode 100644 index 000000000..41e0d0ed8 --- /dev/null +++ b/Tests/ASConfigurationTests.m @@ -0,0 +1,77 @@ +// +// ASConfigurationTests.m +// Texture +// +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import "ASTestCase.h" +#import "ASConfiguration.h" +#import "ASConfigurationDelegate.h" +#import "ASConfigurationInternal.h" + +@interface ASConfigurationTests : ASTestCase + +@end + +@implementation ASConfigurationTests { + void (^onActivate)(ASConfigurationTests *self, ASExperimentalFeatures feature); +} + +- (void)testExperimentalFeatureConfig +{ + // Set the config + ASConfiguration *config = [[ASConfiguration alloc] initWithDictionary:nil]; + config.experimentalFeatures = ASExperimentalGraphicsContexts; + config.delegate = self; + [ASConfigurationManager test_resetWithConfiguration:config]; + + // Set an expectation for a callback, and assert we only get one. + XCTestExpectation *e = [self expectationWithDescription:@"Callback 1 done."]; + onActivate = ^(ASConfigurationTests *self, ASExperimentalFeatures feature) { + XCTAssertEqual(feature, ASExperimentalGraphicsContexts); + [e fulfill]; + // Next time it's a fail. + self->onActivate = ^(ASConfigurationTests *self, ASExperimentalFeatures feature) { + XCTFail(@"Too many callbacks."); + }; + }; + + // Now activate the graphics experiment and expect it works. + XCTAssertTrue(ASActivateExperimentalFeature(ASExperimentalGraphicsContexts)); + // We should get a callback here + // Now activate text node and expect it fails. + XCTAssertFalse(ASActivateExperimentalFeature(ASExperimentalTextNode)); + [self waitForExpectationsWithTimeout:3 handler:nil]; +} + +- (void)textureDidActivateExperimentalFeatures:(ASExperimentalFeatures)feature +{ + if (onActivate) { + onActivate(self, feature); + } +} + +- (void)testMappingNamesToFlags +{ + // Throw in a bad bit. + ASExperimentalFeatures features = ASExperimentalTextNode | ASExperimentalGraphicsContexts | (1 << 22); + NSArray *expectedNames = @[ @"exp_graphics_contexts", @"exp_text_node" ]; + XCTAssertEqualObjects(expectedNames, ASExperimentalFeaturesGetNames(features)); +} + +- (void)testMappingFlagsFromNames +{ + // Throw in a bad name. + NSArray *names = @[ @"exp_text_node", @"exp_graphics_contexts", @"__invalid_name" ]; + ASExperimentalFeatures expected = ASExperimentalGraphicsContexts | ASExperimentalTextNode; + XCTAssertEqual(expected, ASExperimentalFeaturesFromArray(names)); +} + +@end diff --git a/Tests/ASDisplayNodeImplicitHierarchyTests.m b/Tests/ASDisplayNodeImplicitHierarchyTests.m index d033ac4f6..9c39386df 100644 --- a/Tests/ASDisplayNodeImplicitHierarchyTests.m +++ b/Tests/ASDisplayNodeImplicitHierarchyTests.m @@ -131,7 +131,7 @@ - (void)testInitialNodeInsertionWhenEnterPreloadState ASDisplayNodeSizeToFitSizeRange(node, ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))); [node recursivelySetInterfaceState:ASInterfaceStatePreload]; - ASCATransactionQueueWait(); + ASCATransactionQueueWait(nil); // No premature view allocation XCTAssertFalse(node.isNodeLoaded); // Subnodes should be inserted, laid out and entered preload state diff --git a/Tests/ASDisplayNodeTests.mm b/Tests/ASDisplayNodeTests.mm index 1176a709c..50b340890 100644 --- a/Tests/ASDisplayNodeTests.mm +++ b/Tests/ASDisplayNodeTests.mm @@ -131,7 +131,7 @@ @implementation ASTestDisplayNode - (void)setInterfaceState:(ASInterfaceState)state { [super setInterfaceState:state]; - ASCATransactionQueueWait(); + ASCATransactionQueueWait(nil); } - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize diff --git a/Tests/ASDisplayNodeTestsHelper.h b/Tests/ASDisplayNodeTestsHelper.h index 98b4719c6..3e0bea5fb 100644 --- a/Tests/ASDisplayNodeTestsHelper.h +++ b/Tests/ASDisplayNodeTestsHelper.h @@ -18,7 +18,7 @@ #import #import -@class ASDisplayNode; +@class ASCATransactionQueue, ASDisplayNode; typedef BOOL (^as_condition_block_t)(void); @@ -28,6 +28,6 @@ BOOL ASDisplayNodeRunRunLoopUntilBlockIsTrue(as_condition_block_t block); void ASDisplayNodeSizeToFitSize(ASDisplayNode *node, CGSize size); void ASDisplayNodeSizeToFitSizeRange(ASDisplayNode *node, ASSizeRange sizeRange); -void ASCATransactionQueueWait(void); +void ASCATransactionQueueWait(ASCATransactionQueue *q); // nil means shared queue ASDISPLAYNODE_EXTERN_C_END diff --git a/Tests/ASDisplayNodeTestsHelper.m b/Tests/ASDisplayNodeTestsHelper.m index 0a37da1e4..6dbd9b794 100644 --- a/Tests/ASDisplayNodeTestsHelper.m +++ b/Tests/ASDisplayNodeTestsHelper.m @@ -64,12 +64,13 @@ void ASDisplayNodeSizeToFitSizeRange(ASDisplayNode *node, ASSizeRange sizeRange) node.bounds = (CGRect){.origin = CGPointZero, .size = sizeThatFits}; } -void ASCATransactionQueueWait(void) +void ASCATransactionQueueWait(ASCATransactionQueue *q) { + if (!q) { q = ASCATransactionQueue.sharedQueue; } NSDate *date = [NSDate dateWithTimeIntervalSinceNow:1]; BOOL whileResult = YES; while ([date timeIntervalSinceNow] > 0 && - (whileResult = ![[ASCATransactionQueue sharedQueue] isEmpty])) { + (whileResult = ![q isEmpty])) { [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow:0.01]]; } diff --git a/Tests/ASNetworkImageNodeTests.m b/Tests/ASNetworkImageNodeTests.m index 94dc668d5..498e79e8c 100644 --- a/Tests/ASNetworkImageNodeTests.m +++ b/Tests/ASNetworkImageNodeTests.m @@ -2,8 +2,12 @@ // ASNetworkImageNodeTests.m // Texture // -// Created by Adlai Holler on 10/14/16. -// Copyright © 2016 Facebook. All rights reserved. +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASPerformanceTestContext.h b/Tests/ASPerformanceTestContext.h index 42075f5fc..d60f53b1a 100644 --- a/Tests/ASPerformanceTestContext.h +++ b/Tests/ASPerformanceTestContext.h @@ -2,8 +2,12 @@ // ASPerformanceTestContext.h // Texture // -// Created by Adlai Holler on 8/28/16. -// Copyright © 2016 Facebook. All rights reserved. +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASRunLoopQueueTests.m b/Tests/ASRunLoopQueueTests.m index 2fc9c04b5..b195cbc4d 100644 --- a/Tests/ASRunLoopQueueTests.m +++ b/Tests/ASRunLoopQueueTests.m @@ -10,7 +10,8 @@ // http://www.apache.org/licenses/LICENSE-2.0 // -#import +#import "ASTestCase.h" + #import #import "ASDisplayNodeTestsHelper.h" @@ -27,7 +28,7 @@ - (void)prepareForCATransactionCommit } @end -@interface ASRunLoopQueueTests : XCTestCase +@interface ASRunLoopQueueTests : ASTestCase @end @@ -171,12 +172,18 @@ - (void)testWeakQueueWithAllDeallocatedObjectsIsDrained - (void)testASCATransactionQueueDisable { + // Disable coalescing. + ASConfiguration *config = [[ASConfiguration alloc] init]; + config.experimentalFeatures = kNilOptions; + [ASConfigurationManager test_resetWithConfiguration:config]; + ASCATransactionQueue *queue = [[ASCATransactionQueue alloc] init]; - [queue disable]; QueueObject *object = [[QueueObject alloc] init]; - [[ASCATransactionQueue sharedQueue] enqueue:object]; + XCTAssertFalse(object.queueObjectProcessed); + [queue enqueue:object]; + XCTAssertTrue(object.queueObjectProcessed); XCTAssertTrue([queue isEmpty]); - XCTAssertTrue([queue disabled]); + XCTAssertFalse(queue.enabled); } - (void)testASCATransactionQueueProcess @@ -185,8 +192,9 @@ - (void)testASCATransactionQueueProcess QueueObject *object = [[QueueObject alloc] init]; [queue enqueue:object]; XCTAssertFalse(object.queueObjectProcessed); - ASCATransactionQueueWait(); + ASCATransactionQueueWait(queue); XCTAssertTrue(object.queueObjectProcessed); + XCTAssertTrue(queue.enabled); } @end diff --git a/Tests/ASTextNodePerformanceTests.m b/Tests/ASTextNodePerformanceTests.m index aecf6d2fd..f5c62e53a 100644 --- a/Tests/ASTextNodePerformanceTests.m +++ b/Tests/ASTextNodePerformanceTests.m @@ -2,8 +2,12 @@ // ASTextNodePerformanceTests.m // Texture // -// Created by Adlai Holler on 8/28/16. -// Copyright © 2016 Facebook. All rights reserved. +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import diff --git a/Tests/ASTextNodeTests.m b/Tests/ASTextNodeTests.m index 5b1f2cf4d..8c90a6959 100644 --- a/Tests/ASTextNodeTests.m +++ b/Tests/ASTextNodeTests.m @@ -17,6 +17,8 @@ #import +#import "ASTestCase.h" + #import #import @@ -31,6 +33,10 @@ @interface ASTextNodeTestDelegate : NSObject @property (nonatomic, copy, readonly) NSString *tappedLinkAttribute; @property (nonatomic, assign, readonly) id tappedLinkValue; +@end +@interface ASTextNodeSubclass : ASTextNode +@end +@interface ASTextNodeSecondSubclass : ASTextNodeSubclass @end @implementation ASTextNodeTestDelegate @@ -235,4 +241,23 @@ - (void)testAddingExclusionPathsShouldInvalidateAndIncreaseTheSize XCTAssertGreaterThan(sizeWithExclusionPaths.height, sizeWithoutExclusionPaths.height, @"Setting exclusions paths should invalidate the calculated size and return a greater size"); } +- (void)testThatTheExperimentWorksCorrectly +{ + ASConfiguration *config = [ASConfiguration new]; + config.experimentalFeatures = ASExperimentalTextNode; + [ASConfigurationManager test_resetWithConfiguration:config]; + + ASTextNode *plainTextNode = [[ASTextNode alloc] init]; + XCTAssertEqualObjects(plainTextNode.class, [ASTextNode2 class]); + + ASTextNodeSecondSubclass *sc2 = [[ASTextNodeSecondSubclass alloc] init]; + XCTAssertEqualObjects([ASTextNodeSubclass superclass], [ASTextNode2 class]); + XCTAssertEqualObjects(sc2.superclass, [ASTextNodeSubclass class]); +} + +@end + +@implementation ASTextNodeSubclass +@end +@implementation ASTextNodeSecondSubclass @end diff --git a/Tests/ASVideoNodeTests.m b/Tests/ASVideoNodeTests.m index 96892b176..24bb5dd37 100644 --- a/Tests/ASVideoNodeTests.m +++ b/Tests/ASVideoNodeTests.m @@ -352,7 +352,7 @@ - (void)testVideoResumedWhenBufferIsLikelyToKeepUp [_videoNode setInterfaceState:ASInterfaceStateVisible | ASInterfaceStateDisplay | ASInterfaceStatePreload]; [_videoNode prepareToPlayAsset:assetMock withKeys:_requestedKeys]; - ASCATransactionQueueWait(); + ASCATransactionQueueWait(nil); [_videoNode pause]; _videoNode.shouldBePlaying = YES; XCTAssertFalse(_videoNode.isPlaying); diff --git a/Tests/Common/ASTestCase.h b/Tests/Common/ASTestCase.h index 54231b64e..bbcf07336 100644 --- a/Tests/Common/ASTestCase.h +++ b/Tests/Common/ASTestCase.h @@ -2,8 +2,13 @@ // ASTestCase.h // Texture // -// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // @@ -16,6 +21,7 @@ #import #import #import "OCMockObject+ASAdditions.h" +#import "ASConfigurationInternal.h" NS_ASSUME_NONNULL_BEGIN @@ -25,4 +31,10 @@ NS_ASSUME_NONNULL_BEGIN @end +@interface ASConfigurationManager (Testing) + ++ (void)test_resetWithConfiguration:(nullable ASConfiguration *)configuration; + +@end + NS_ASSUME_NONNULL_END diff --git a/Tests/Common/ASTestCase.m b/Tests/Common/ASTestCase.m index 5bc720253..fb059e7ed 100644 --- a/Tests/Common/ASTestCase.m +++ b/Tests/Common/ASTestCase.m @@ -2,8 +2,13 @@ // ASTestCase.m // Texture // -// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. +// +// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // @@ -31,6 +36,8 @@ - (void)setUp - (void)tearDown { + [ASConfigurationManager test_resetWithConfiguration:nil]; + // Clear out all application windows. Note: the system will retain these sometimes on its // own but we'll do our best. for (UIWindow *window in [UIApplication sharedApplication].windows) { diff --git a/examples/ASDKgram/Sample.xcodeproj/project.pbxproj b/examples/ASDKgram/Sample.xcodeproj/project.pbxproj index 743b9456e..a4b5b7722 100644 --- a/examples/ASDKgram/Sample.xcodeproj/project.pbxproj +++ b/examples/ASDKgram/Sample.xcodeproj/project.pbxproj @@ -36,6 +36,7 @@ CC5532171E15CC1E0011C01F /* ASCollectionSectionController.m in Sources */ = {isa = PBXBuildFile; fileRef = CC5532161E15CC1E0011C01F /* ASCollectionSectionController.m */; }; CC6350BB1E1C482D002BC613 /* TailLoadingNode.m in Sources */ = {isa = PBXBuildFile; fileRef = CC6350BA1E1C482D002BC613 /* TailLoadingNode.m */; }; CC85250F1E36B392008EABE6 /* FeedHeaderNode.m in Sources */ = {isa = PBXBuildFile; fileRef = CC85250E1E36B392008EABE6 /* FeedHeaderNode.m */; }; + CCEDDDD7200C4C0E00FFCD0A /* TextureConfigDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CCEDDDD6200C4C0E00FFCD0A /* TextureConfigDelegate.m */; }; E5F128F01E09625400B4335F /* PhotoFeedBaseController.m in Sources */ = {isa = PBXBuildFile; fileRef = E5F128EF1E09625400B4335F /* PhotoFeedBaseController.m */; }; /* End PBXBuildFile section */ @@ -98,6 +99,7 @@ CC6350BA1E1C482D002BC613 /* TailLoadingNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TailLoadingNode.m; sourceTree = ""; }; CC85250D1E36B392008EABE6 /* FeedHeaderNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FeedHeaderNode.h; sourceTree = ""; }; CC85250E1E36B392008EABE6 /* FeedHeaderNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FeedHeaderNode.m; sourceTree = ""; }; + CCEDDDD6200C4C0E00FFCD0A /* TextureConfigDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TextureConfigDelegate.m; sourceTree = ""; }; D09B5DF0BFB37583DE8F3142 /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = ""; }; E5F128EE1E09612700B4335F /* PhotoFeedBaseController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhotoFeedBaseController.h; sourceTree = ""; }; E5F128EF1E09625400B4335F /* PhotoFeedBaseController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PhotoFeedBaseController.m; sourceTree = ""; }; @@ -141,6 +143,7 @@ children = ( 768843511CAA37EF00D8629E /* AppDelegate.h */, 768843681CAA37EF00D8629E /* AppDelegate.m */, + CCEDDDD6200C4C0E00FFCD0A /* TextureConfigDelegate.m */, 76229A761CBB79E000B62CEF /* WindowWithStatusBarUnderlay.h */, 76229A771CBB79E000B62CEF /* WindowWithStatusBarUnderlay.m */, 767A5F141CAA3D8A004CDA8D /* Controller */, @@ -427,6 +430,7 @@ CC5532171E15CC1E0011C01F /* ASCollectionSectionController.m in Sources */, 768843801CAA37EF00D8629E /* AppDelegate.m in Sources */, 768843811CAA37EF00D8629E /* CommentFeedModel.m in Sources */, + CCEDDDD7200C4C0E00FFCD0A /* TextureConfigDelegate.m in Sources */, 7688438E1CAA37EF00D8629E /* PhotoFeedNodeController.m in Sources */, CC6350BB1E1C482D002BC613 /* TailLoadingNode.m in Sources */, CC85250F1E36B392008EABE6 /* FeedHeaderNode.m in Sources */, diff --git a/examples/ASDKgram/Sample/AppDelegate.m b/examples/ASDKgram/Sample/AppDelegate.m index 21c47a273..e1dd507ba 100644 --- a/examples/ASDKgram/Sample/AppDelegate.m +++ b/examples/ASDKgram/Sample/AppDelegate.m @@ -7,7 +7,7 @@ // LICENSE file in the /ASDK-Licenses directory of this source tree. An additional // grant of patent rights can be found in the PATENTS file in the same directory. // -// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present, +// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present, // Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -40,8 +40,6 @@ @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - ASEnableNoCopyRendering(); - // this UIWindow subclass is neccessary to make the status bar opaque _window = [[WindowWithStatusBarUnderlay alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; _window.backgroundColor = [UIColor whiteColor]; diff --git a/examples/ASDKgram/Sample/TextureConfigDelegate.m b/examples/ASDKgram/Sample/TextureConfigDelegate.m new file mode 100644 index 000000000..0905a646e --- /dev/null +++ b/examples/ASDKgram/Sample/TextureConfigDelegate.m @@ -0,0 +1,41 @@ +// +// TextureConfigDelegate.m +// Texture +// +// Copyright (c) 2018-present, Pinterest, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface TextureConfigDelegate : NSObject + +@end + +@implementation ASConfiguration (UserProvided) + ++ (ASConfiguration *)textureConfiguration +{ + ASConfiguration *config = [[ASConfiguration alloc] init]; + config.experimentalFeatures = ASExperimentalGraphicsContexts | ASExperimentalTextNode; + config.delegate = [[TextureConfigDelegate alloc] init]; + return config; +} + +@end + +@implementation TextureConfigDelegate + +- (void)textureDidActivateExperimentalFeatures:(ASExperimentalFeatures)features +{ + if (features & ASExperimentalGraphicsContexts) { + NSLog(@"Texture activated experimental graphics contexts."); + } +} + +@end + diff --git a/examples/Kittens/Sample/AppDelegate.m b/examples/Kittens/Sample/AppDelegate.m index 230173d51..e683d16d4 100644 --- a/examples/Kittens/Sample/AppDelegate.m +++ b/examples/Kittens/Sample/AppDelegate.m @@ -1,18 +1,18 @@ // // AppDelegate.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "AppDelegate.h" @@ -24,7 +24,6 @@ @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [ASTextNode setExperimentOptions:ASTextNodeExperimentRandomInstances]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] init]]; diff --git a/examples/Kittens/Sample/ViewController.m b/examples/Kittens/Sample/ViewController.m index 63a662327..38d01d6f5 100644 --- a/examples/Kittens/Sample/ViewController.m +++ b/examples/Kittens/Sample/ViewController.m @@ -1,18 +1,18 @@ // // ViewController.m -// Sample +// Texture // // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. An additional grant -// of patent rights can be found in the PATENTS file in the same directory. +// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional +// grant of patent rights can be found in the PATENTS file in the same directory. // -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present, +// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ViewController.h" @@ -35,12 +35,10 @@ @interface ViewController () // array of boxed CGSizes corresponding to placekitten.com kittens NSMutableArray *_kittenDataSource; - BOOL _dataSourceLocked; NSIndexPath *_blurbNodeIndexPath; } @property (nonatomic, strong) NSMutableArray *kittenDataSource; -@property (atomic, assign) BOOL dataSourceLocked; @end @@ -96,12 +94,6 @@ - (NSMutableArray *)createLitterWithSize:(NSInteger)litterSize return kittens; } -- (void)setKittenDataSource:(NSMutableArray *)kittenDataSource { - ASDisplayNodeAssert(!self.dataSourceLocked, @"Could not update data source when it is locked !"); - - _kittenDataSource = kittenDataSource; -} - - (void)toggleEditingMode { [_tableNode.view setEditing:!_tableNode.view.editing animated:YES];