From 60478a18cff019c15d0bb6fcceb193702e0e2804 Mon Sep 17 00:00:00 2001 From: Peter Baumgartner Date: Sun, 14 Oct 2018 17:22:44 +0200 Subject: [PATCH 01/17] Only needed for macOS target --- BXSwiftUtils.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/BXSwiftUtils.xcodeproj/project.pbxproj b/BXSwiftUtils.xcodeproj/project.pbxproj index ab71041..5498a94 100644 --- a/BXSwiftUtils.xcodeproj/project.pbxproj +++ b/BXSwiftUtils.xcodeproj/project.pbxproj @@ -47,7 +47,6 @@ 4875F27B20529A19009985EC /* Collection+SafeAccessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4875F2542051EF7F009985EC /* Collection+SafeAccessTests.swift */; }; 487A39CD2069174E007BEC66 /* TypedKVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487A39CC2069174E007BEC66 /* TypedKVO.swift */; }; 487A39CE2069174E007BEC66 /* TypedKVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487A39CC2069174E007BEC66 /* TypedKVO.swift */; }; - 487A39D020691E06007BEC66 /* NSArrayController+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487A39CF20691E05007BEC66 /* NSArrayController+UIKit.swift */; }; 487A39D120691E06007BEC66 /* NSArrayController+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487A39CF20691E05007BEC66 /* NSArrayController+UIKit.swift */; }; 487A39D220693A25007BEC66 /* KVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487A39C920690635007BEC66 /* KVO.swift */; }; 487A39D320693A25007BEC66 /* KVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487A39C920690635007BEC66 /* KVO.swift */; }; @@ -824,7 +823,6 @@ D03F13B820F36D57004C8306 /* MTLComputeCommandEncoder+Extensions.swift in Sources */, D0EA52DA20F8992300A0EF6D /* MTKTextureLoader+Extensions.swift in Sources */, 48A971DA2158F75800215F9F /* Equatable+IsContainedIn.swift in Sources */, - 487A39D020691E06007BEC66 /* NSArrayController+UIKit.swift in Sources */, 4875F2512051EEF5009985EC /* Collection+SafeAccess.swift in Sources */, D0859FD12158F7DD00F624C7 /* URL+Contents.swift in Sources */, 4875F2262051E9D0009985EC /* Array+Concatenation.swift in Sources */, From f9b15187269b80279325a1083bf177f49deb1a06 Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Mon, 15 Oct 2018 11:17:06 +0200 Subject: [PATCH 02/17] travis.yml: Add dummy deploy phase --- .gitignore | 1 + .travis.yml | 11 +++++++ BXSwiftUtils.xcodeproj/project.pbxproj | 43 ++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/.gitignore b/.gitignore index 47f8ac6..9c9f303 100644 --- a/.gitignore +++ b/.gitignore @@ -67,3 +67,4 @@ fastlane/screenshots fastlane/test_output .DS_Store +.bx_product_path diff --git a/.travis.yml b/.travis.yml index 90dfb00..f8c01ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,9 +17,20 @@ matrix: env: SCHEME="BXSwiftUtils-macOS" DESTINATION="platform=macOS,arch=x86_64" - osx_image: xcode10 env: SCHEME="BXSwiftUtils-iOS" DESTINATION="platform=iOS Simulator,name=iPhone X,OS=12.0" + - osx_image: xcode10 + script: echo "I will deploy..." script: set -o pipefail; xcodebuild -scheme "$SCHEME" -destination "$DESTINATION" clean build test | xcpretty notifications: slack: template: - "%{repository_slug}@%{branch} (<%{compare_url}|%{commit}>) by %{author} %{result} in %{duration}" secure: UovCzpghaeeBGXjTjvZGMr0jWpxwdOaT6tkGlzdOjwaMK8yS8EId7pP3/voe5FFt60t9QJtXi53dy//jccfCOKGDqmAyn71QnSG3Pw5wuBdNwTS+6EMWFJeJzcp/5mvbdxzfTUnmgdJYAUdcfQqcSdccQe/lPr9kvxO5nDuXGI+Tboofb2/DazktmRrbM8Dlq61q2jYrZ4KQihtBLlcl3+XZsWL2+Qfs4arG5S7DZjhrDWpTUYw8FPvumsIZ8j4PuvfKDEqB8J139bRMuj+NbLO+7Ki2Wucg96XPPf7co4qsLM8FMUN5GG3DReIKqR4+K8BQadpp8hJXtNqfRSG8miqMMe7hONwLXcVGHALCUHEy19JhWLeHYdpMMoVTnFG9qG4X39GDdCpPphCRCRT0HqJyljpIj1PSXPHc2W1Zy6mQa9C6bJ2zN9CGBmG6Pf5/glbtx7ahmUQcnFMgnysnupffa01akDjOvDs742lyxfKdAHasvY27TtOwh6yjUa5HrSuYXo5XZ3oYslpUAAeWEOpykNhgmRnUU5tkLMH8/R3CGMA8hyTPnbLXFElmycSiu8pChXFY8B7EbjqnbGGir3ebsNyUtByEUwKgbMRz6EYJZoMVCVRcY5pd93Kyqe4nYJSohrrabhwVoa1GoEJHtKeypC9e0UIgAYtkHEg6Rlc= +deploy: + provider: releases + skip_cleanup: true + api_key: + secure: ezqVw0RZCz55IKJNs3mQboM2r1WbiyJnXI1gCMsMOicvp++NXR1X7eJVdJG/uho+/Pb1fQMPjyQeLUoAvVZste1LovnfJy+0PYup2fvQHGPtzntpaVUBSmPEvTRsOVbWdMkHnxztTdJZYoUMfQHtdm1R3rFSen41FNplc3INAeNtYVeGXVXDpRJ7lwOov+G1SgmMlYns10eT2S+6Iw/F9BK8eQMwShtM8JedI+Hy9e5UQ9O3qk36g2neYDTLR8fKVp8Gq7rUta5qP/j1ZcC9BZHawzsivaAfWio3PpV4UZXr7I+w1JQp9PQdbNJ4VZqYNBgwgRSF5eLHD6WV9oxhFMkZ0evh/90quFN+FTzXz16u2aSOJ/E83aqoaFVTy4ZNFppt4MmDlWLGW1AZDJyJt4a4wX7If06UPlQ1G3mR6kJxwgFW/wRxAKeSF1xiAeizA/FYnNiOfuq942LB+1BOTokE+50EFME9nvxYQzr66Z3v+G9SLFeDxTtEYsmjqOAJSKLf4wKG11cpYNzY+E0eRuxZGq278ukbYLkH2d8IEKKqRmTaan/ZhNAZUsS9go1hBlBJMiLgTCbCKuzb3GiqaQRQ71j8/sK2anPCpogJVidgv2gpHXg1E5tlXNevUJNlmBPtWReRRaNTLbEgNIayLwF2wh8IPgle3PxrtGoMPPA= + file: Artifacts/BXSwiftUtils.framework + on: + repo: boinx/BXSwiftUtils + tags: true diff --git a/BXSwiftUtils.xcodeproj/project.pbxproj b/BXSwiftUtils.xcodeproj/project.pbxproj index 5498a94..d1c1755 100644 --- a/BXSwiftUtils.xcodeproj/project.pbxproj +++ b/BXSwiftUtils.xcodeproj/project.pbxproj @@ -657,6 +657,7 @@ 4875F2032051E70E009985EC /* Frameworks */, 4875F2042051E70E009985EC /* Headers */, 4875F2052051E70E009985EC /* Resources */, + 481A257921748DBE00FB8E9A /* Export Product Location */, ); buildRules = ( ); @@ -693,6 +694,7 @@ 4875F25B205299A6009985EC /* Frameworks */, 4875F25C205299A6009985EC /* Headers */, 4875F25D205299A6009985EC /* Resources */, + 481A257821748DAC00FB8E9A /* Export Product Location */, ); buildRules = ( ); @@ -804,6 +806,47 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 481A257821748DAC00FB8E9A /* Export Product Location */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Export Product Location"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"Write product path to .bx_product_path\"\necho \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME\" > $SRCROOT/.bx_product_path\n"; + showEnvVarsInLog = 0; + }; + 481A257921748DBE00FB8E9A /* Export Product Location */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Export Product Location"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"Write product path to .bx_product_path\"\necho \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME\" > $SRCROOT/.bx_product_path\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 4875F2022051E70E009985EC /* Sources */ = { isa = PBXSourcesBuildPhase; From fbfdeee647156663fb367ed5d2b6d865a9be4291 Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Mon, 15 Oct 2018 11:34:07 +0200 Subject: [PATCH 03/17] Attempt deployment --- .gitignore | 1 + .travis.yml | 4 ++-- BXSwiftUtils.xcodeproj/project.pbxproj | 20 ++++++++++++++----- deploy.sh | 27 ++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 deploy.sh diff --git a/.gitignore b/.gitignore index 9c9f303..211170f 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,4 @@ fastlane/test_output .DS_Store .bx_product_path +Artifacts diff --git a/.travis.yml b/.travis.yml index f8c01ca..76ec7e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ matrix: - osx_image: xcode10 env: SCHEME="BXSwiftUtils-iOS" DESTINATION="platform=iOS Simulator,name=iPhone X,OS=12.0" - osx_image: xcode10 - script: echo "I will deploy..." + script: sh deploy.sh script: set -o pipefail; xcodebuild -scheme "$SCHEME" -destination "$DESTINATION" clean build test | xcpretty notifications: slack: @@ -30,7 +30,7 @@ deploy: skip_cleanup: true api_key: secure: ezqVw0RZCz55IKJNs3mQboM2r1WbiyJnXI1gCMsMOicvp++NXR1X7eJVdJG/uho+/Pb1fQMPjyQeLUoAvVZste1LovnfJy+0PYup2fvQHGPtzntpaVUBSmPEvTRsOVbWdMkHnxztTdJZYoUMfQHtdm1R3rFSen41FNplc3INAeNtYVeGXVXDpRJ7lwOov+G1SgmMlYns10eT2S+6Iw/F9BK8eQMwShtM8JedI+Hy9e5UQ9O3qk36g2neYDTLR8fKVp8Gq7rUta5qP/j1ZcC9BZHawzsivaAfWio3PpV4UZXr7I+w1JQp9PQdbNJ4VZqYNBgwgRSF5eLHD6WV9oxhFMkZ0evh/90quFN+FTzXz16u2aSOJ/E83aqoaFVTy4ZNFppt4MmDlWLGW1AZDJyJt4a4wX7If06UPlQ1G3mR6kJxwgFW/wRxAKeSF1xiAeizA/FYnNiOfuq942LB+1BOTokE+50EFME9nvxYQzr66Z3v+G9SLFeDxTtEYsmjqOAJSKLf4wKG11cpYNzY+E0eRuxZGq278ukbYLkH2d8IEKKqRmTaan/ZhNAZUsS9go1hBlBJMiLgTCbCKuzb3GiqaQRQ71j8/sK2anPCpogJVidgv2gpHXg1E5tlXNevUJNlmBPtWReRRaNTLbEgNIayLwF2wh8IPgle3PxrtGoMPPA= - file: Artifacts/BXSwiftUtils.framework + file: Artifacts/**/* on: repo: boinx/BXSwiftUtils tags: true diff --git a/BXSwiftUtils.xcodeproj/project.pbxproj b/BXSwiftUtils.xcodeproj/project.pbxproj index d1c1755..0a40a72 100644 --- a/BXSwiftUtils.xcodeproj/project.pbxproj +++ b/BXSwiftUtils.xcodeproj/project.pbxproj @@ -189,6 +189,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 481A257B2174939600FB8E9A /* deploy.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = deploy.sh; sourceTree = ""; }; 481BE6D22098362C00F12EDF /* Sequence+CompactMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+CompactMap.swift"; sourceTree = ""; }; 481BE6D52098368E00F12EDF /* Sequence+CompactMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+CompactMap.swift"; sourceTree = ""; }; 4826E57D208F533400A5BA9B /* Array+Decodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Decodable.swift"; sourceTree = ""; }; @@ -318,6 +319,19 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 481A257D217493D400FB8E9A /* Config */ = { + isa = PBXGroup; + children = ( + 484133732052D0C500DDB1E2 /* README.md */, + 4875F2232051E7F4009985EC /* STYLEGUIDE.md */, + 4875F25820528D28009985EC /* LICENSE */, + 487E623020986792007C4A8A /* .gitignore */, + 487E62312098679A007C4A8A /* .travis.yml */, + 481A257B2174939600FB8E9A /* deploy.sh */, + ); + name = Config; + sourceTree = ""; + }; 4853E3882056BDB500938B82 /* Threading */ = { isa = PBXGroup; children = ( @@ -330,11 +344,7 @@ 4875F1FD2051E70E009985EC = { isa = PBXGroup; children = ( - 484133732052D0C500DDB1E2 /* README.md */, - 4875F2232051E7F4009985EC /* STYLEGUIDE.md */, - 4875F25820528D28009985EC /* LICENSE */, - 487E623020986792007C4A8A /* .gitignore */, - 487E62312098679A007C4A8A /* .travis.yml */, + 481A257D217493D400FB8E9A /* Config */, 4875F2092051E70E009985EC /* BXSwiftUtils */, 4875F2142051E70E009985EC /* BXSwiftUtilsTests */, 4875F2082051E70E009985EC /* Products */, diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..1817083 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -e +set -x + +_artifacts="Artifacts" +rm -Rf "$_artifacts" +mkdir "$_artifacts" + +set -o pipefail; xcodebuild -scheme "BXSwiftUtils-macOS" -configuration "Release" clean build | xcpretty + +mkdir -p "$_artifacts/macOS" +cp -R $(cat .bx_product_path) "$_artifacts/macOS/" + +set -o pipefail; xcodebuild -scheme "BXSwiftUtils-iOS" -configuration "Release" clean build | xcpretty + +mkdir -p "$_artifacts/iOS" +cp -R $(cat .bx_product_path) "$_artifacts/iOS/" + +# Delete git tag if already existing +_git_tag="latest" +git tag -d ${_git_tag} +git push origin :refs/tags/${_git_tag} + +# Create new "latest" tag +_commit_hash=$(git rev-parse HEAD) +git tag -f -m "Travis tagged with ${_git_tag}" -a "${_git_tag}" "${_commit_hash}" +git push --tags From d2345ee1fefd32f83b4002178fcb8fa7884f53ae Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Mon, 15 Oct 2018 11:40:17 +0200 Subject: [PATCH 04/17] =?UTF-8?q?Don=E2=80=99t=20push=20tags=20to=20remote?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy.sh b/deploy.sh index 1817083..071832c 100644 --- a/deploy.sh +++ b/deploy.sh @@ -19,9 +19,9 @@ cp -R $(cat .bx_product_path) "$_artifacts/iOS/" # Delete git tag if already existing _git_tag="latest" git tag -d ${_git_tag} -git push origin :refs/tags/${_git_tag} +#git push origin :refs/tags/${_git_tag} # Create new "latest" tag _commit_hash=$(git rev-parse HEAD) git tag -f -m "Travis tagged with ${_git_tag}" -a "${_git_tag}" "${_commit_hash}" -git push --tags +#git push --tags From 772b824fb94a35335fe0e76754487c8d850d2c9c Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Mon, 15 Oct 2018 11:46:22 +0200 Subject: [PATCH 05/17] =?UTF-8?q?Don=E2=80=99t=20remove=20existing=20git?= =?UTF-8?q?=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy.sh b/deploy.sh index 071832c..d81066f 100644 --- a/deploy.sh +++ b/deploy.sh @@ -18,7 +18,7 @@ cp -R $(cat .bx_product_path) "$_artifacts/iOS/" # Delete git tag if already existing _git_tag="latest" -git tag -d ${_git_tag} +#git tag -d ${_git_tag} #git push origin :refs/tags/${_git_tag} # Create new "latest" tag From 9b4bf049a9954ebc145178a343ef3307235c64c8 Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Mon, 15 Oct 2018 11:56:56 +0200 Subject: [PATCH 06/17] Try different release trigger --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 76ec7e7..6c81870 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,7 @@ matrix: - osx_image: xcode10 env: SCHEME="BXSwiftUtils-iOS" DESTINATION="platform=iOS Simulator,name=iPhone X,OS=12.0" - osx_image: xcode10 + env: CREATE_RELEASE="true" script: sh deploy.sh script: set -o pipefail; xcodebuild -scheme "$SCHEME" -destination "$DESTINATION" clean build test | xcpretty notifications: @@ -33,4 +34,4 @@ deploy: file: Artifacts/**/* on: repo: boinx/BXSwiftUtils - tags: true + condition: $CREATE_RELEASE = true From ddc7dc958040ccac7f16d960df0e3e0a020fa6a8 Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Mon, 15 Oct 2018 14:05:10 +0200 Subject: [PATCH 07/17] =?UTF-8?q?Actually,=20do=20push=20tags=20because=20?= =?UTF-8?q?GH=20releases=20don=E2=80=99t=20work=20without=20them?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .travis.yml | 3 +++ deploy.sh | 14 +++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6c81870..45605cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,9 @@ os: osx xcode_project: BXSwiftUtils.xcodeproj before_install: - gem install xcpretty +env: + # GH_TOKEN + - secure: Chw5iyaWS4vid3Eoam8ouuhmZ+Gb65FxyJZsHH0wXfgXTR8T3CCx0lJa2JPFjFw8gY+pkvhyVzMGqqDGyiPK80La2Ah5wHWDurvdM9oLVT1OhloQPGKRZIwn35xVVhkQnYz+vs4pPb4/quVAtfMStCUZ4aKbXmrFh5sKQtvXzK3wwYvfWOnbxy9TJk0LYm+EHHktp541TNC2/BBNG7glQp5tzGX/nFxdE58bOWRZzF6ZDILd/hzDMCw8kyUC8305c+WqivpWK+n8yC4rT+pGvJt2RstcjcS8I2kb4ZNSBoiVT8ZWXFMP/dwSXDPMXdi8OJNfd3MGPC5SXeY1rpTNidbzwkhnlQLOqcUVVUeQtZfhfukjw2EFj0b9c4kKfE7zQxUHShcC6/iNCz5MplgoCXUZjrCI1Wacr7GUqRh+SZl6dE/oua8FcwihIWRrJdKIn6s0nu18RipkPsI3U1h74792kS1CmaN3oigSj5hJbrg2ygziWf3Zlb8CcpLhqZWJscIPo+MU1/lGOysKJ4ZI6pKRBZdKeu0OYjdx9GGU4ZtAuwR4+3QXtYD4C+KY4DdoKg8Hi/4CRcjyiAafD6yxBUoOGhj2Kgm+9eNnl2zYdPtUC57bHyHb0uMY73QulxJqFYxozYzu1euk9xzsZxvcs676Vph2iUNMFE4HSbz2LY8= matrix: include: - osx_image: xcode9.2 diff --git a/deploy.sh b/deploy.sh index d81066f..2001d61 100644 --- a/deploy.sh +++ b/deploy.sh @@ -17,11 +17,15 @@ mkdir -p "$_artifacts/iOS" cp -R $(cat .bx_product_path) "$_artifacts/iOS/" # Delete git tag if already existing +git remote add origin-authenticated "https://${GH_TOKEN}@github.com/boinx/BXSwiftUtils.git" + +# if deleting the tag fails, that's ok +set +e _git_tag="latest" -#git tag -d ${_git_tag} -#git push origin :refs/tags/${_git_tag} +git tag -d ${_git_tag} +git push origin-authenticated :refs/tags/${_git_tag} +set -e # Create new "latest" tag -_commit_hash=$(git rev-parse HEAD) -git tag -f -m "Travis tagged with ${_git_tag}" -a "${_git_tag}" "${_commit_hash}" -#git push --tags +git tag -f -m "Travis tagged with ${_git_tag}" -a "${_git_tag}" +git push --tags origin-authenticated master From efdbcb68c7080a22511eecc29db2b6a5dac75174 Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Mon, 15 Oct 2018 14:14:24 +0200 Subject: [PATCH 08/17] Properly add GH_TOKEN --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 45605cd..ec8aa0f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,9 @@ xcode_project: BXSwiftUtils.xcodeproj before_install: - gem install xcpretty env: - # GH_TOKEN - - secure: Chw5iyaWS4vid3Eoam8ouuhmZ+Gb65FxyJZsHH0wXfgXTR8T3CCx0lJa2JPFjFw8gY+pkvhyVzMGqqDGyiPK80La2Ah5wHWDurvdM9oLVT1OhloQPGKRZIwn35xVVhkQnYz+vs4pPb4/quVAtfMStCUZ4aKbXmrFh5sKQtvXzK3wwYvfWOnbxy9TJk0LYm+EHHktp541TNC2/BBNG7glQp5tzGX/nFxdE58bOWRZzF6ZDILd/hzDMCw8kyUC8305c+WqivpWK+n8yC4rT+pGvJt2RstcjcS8I2kb4ZNSBoiVT8ZWXFMP/dwSXDPMXdi8OJNfd3MGPC5SXeY1rpTNidbzwkhnlQLOqcUVVUeQtZfhfukjw2EFj0b9c4kKfE7zQxUHShcC6/iNCz5MplgoCXUZjrCI1Wacr7GUqRh+SZl6dE/oua8FcwihIWRrJdKIn6s0nu18RipkPsI3U1h74792kS1CmaN3oigSj5hJbrg2ygziWf3Zlb8CcpLhqZWJscIPo+MU1/lGOysKJ4ZI6pKRBZdKeu0OYjdx9GGU4ZtAuwR4+3QXtYD4C+KY4DdoKg8Hi/4CRcjyiAafD6yxBUoOGhj2Kgm+9eNnl2zYdPtUC57bHyHb0uMY73QulxJqFYxozYzu1euk9xzsZxvcs676Vph2iUNMFE4HSbz2LY8= + global: + # GH_TOKEN + - secure: Chw5iyaWS4vid3Eoam8ouuhmZ+Gb65FxyJZsHH0wXfgXTR8T3CCx0lJa2JPFjFw8gY+pkvhyVzMGqqDGyiPK80La2Ah5wHWDurvdM9oLVT1OhloQPGKRZIwn35xVVhkQnYz+vs4pPb4/quVAtfMStCUZ4aKbXmrFh5sKQtvXzK3wwYvfWOnbxy9TJk0LYm+EHHktp541TNC2/BBNG7glQp5tzGX/nFxdE58bOWRZzF6ZDILd/hzDMCw8kyUC8305c+WqivpWK+n8yC4rT+pGvJt2RstcjcS8I2kb4ZNSBoiVT8ZWXFMP/dwSXDPMXdi8OJNfd3MGPC5SXeY1rpTNidbzwkhnlQLOqcUVVUeQtZfhfukjw2EFj0b9c4kKfE7zQxUHShcC6/iNCz5MplgoCXUZjrCI1Wacr7GUqRh+SZl6dE/oua8FcwihIWRrJdKIn6s0nu18RipkPsI3U1h74792kS1CmaN3oigSj5hJbrg2ygziWf3Zlb8CcpLhqZWJscIPo+MU1/lGOysKJ4ZI6pKRBZdKeu0OYjdx9GGU4ZtAuwR4+3QXtYD4C+KY4DdoKg8Hi/4CRcjyiAafD6yxBUoOGhj2Kgm+9eNnl2zYdPtUC57bHyHb0uMY73QulxJqFYxozYzu1euk9xzsZxvcs676Vph2iUNMFE4HSbz2LY8= matrix: include: - osx_image: xcode9.2 From cbe9b185505f8cc78cdcb66383a37a6e492311a8 Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Mon, 15 Oct 2018 14:30:57 +0200 Subject: [PATCH 09/17] Let travis create tags for us --- .travis.yml | 6 ++++-- deploy.sh | 28 ++++++++++++---------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index ec8aa0f..4cd8f2b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,8 @@ env: global: # GH_TOKEN - secure: Chw5iyaWS4vid3Eoam8ouuhmZ+Gb65FxyJZsHH0wXfgXTR8T3CCx0lJa2JPFjFw8gY+pkvhyVzMGqqDGyiPK80La2Ah5wHWDurvdM9oLVT1OhloQPGKRZIwn35xVVhkQnYz+vs4pPb4/quVAtfMStCUZ4aKbXmrFh5sKQtvXzK3wwYvfWOnbxy9TJk0LYm+EHHktp541TNC2/BBNG7glQp5tzGX/nFxdE58bOWRZzF6ZDILd/hzDMCw8kyUC8305c+WqivpWK+n8yC4rT+pGvJt2RstcjcS8I2kb4ZNSBoiVT8ZWXFMP/dwSXDPMXdi8OJNfd3MGPC5SXeY1rpTNidbzwkhnlQLOqcUVVUeQtZfhfukjw2EFj0b9c4kKfE7zQxUHShcC6/iNCz5MplgoCXUZjrCI1Wacr7GUqRh+SZl6dE/oua8FcwihIWRrJdKIn6s0nu18RipkPsI3U1h74792kS1CmaN3oigSj5hJbrg2ygziWf3Zlb8CcpLhqZWJscIPo+MU1/lGOysKJ4ZI6pKRBZdKeu0OYjdx9GGU4ZtAuwR4+3QXtYD4C+KY4DdoKg8Hi/4CRcjyiAafD6yxBUoOGhj2Kgm+9eNnl2zYdPtUC57bHyHb0uMY73QulxJqFYxozYzu1euk9xzsZxvcs676Vph2iUNMFE4HSbz2LY8= +# Don't build tags as the build script itself creates new tags... +if: tag IS blank matrix: include: - osx_image: xcode9.2 @@ -35,7 +37,7 @@ deploy: skip_cleanup: true api_key: secure: ezqVw0RZCz55IKJNs3mQboM2r1WbiyJnXI1gCMsMOicvp++NXR1X7eJVdJG/uho+/Pb1fQMPjyQeLUoAvVZste1LovnfJy+0PYup2fvQHGPtzntpaVUBSmPEvTRsOVbWdMkHnxztTdJZYoUMfQHtdm1R3rFSen41FNplc3INAeNtYVeGXVXDpRJ7lwOov+G1SgmMlYns10eT2S+6Iw/F9BK8eQMwShtM8JedI+Hy9e5UQ9O3qk36g2neYDTLR8fKVp8Gq7rUta5qP/j1ZcC9BZHawzsivaAfWio3PpV4UZXr7I+w1JQp9PQdbNJ4VZqYNBgwgRSF5eLHD6WV9oxhFMkZ0evh/90quFN+FTzXz16u2aSOJ/E83aqoaFVTy4ZNFppt4MmDlWLGW1AZDJyJt4a4wX7If06UPlQ1G3mR6kJxwgFW/wRxAKeSF1xiAeizA/FYnNiOfuq942LB+1BOTokE+50EFME9nvxYQzr66Z3v+G9SLFeDxTtEYsmjqOAJSKLf4wKG11cpYNzY+E0eRuxZGq278ukbYLkH2d8IEKKqRmTaan/ZhNAZUsS9go1hBlBJMiLgTCbCKuzb3GiqaQRQ71j8/sK2anPCpogJVidgv2gpHXg1E5tlXNevUJNlmBPtWReRRaNTLbEgNIayLwF2wh8IPgle3PxrtGoMPPA= - file: Artifacts/**/* + file: Artifacts/* + file_glob: true on: - repo: boinx/BXSwiftUtils condition: $CREATE_RELEASE = true diff --git a/deploy.sh b/deploy.sh index 2001d61..64f25ad 100644 --- a/deploy.sh +++ b/deploy.sh @@ -7,25 +7,21 @@ rm -Rf "$_artifacts" mkdir "$_artifacts" set -o pipefail; xcodebuild -scheme "BXSwiftUtils-macOS" -configuration "Release" clean build | xcpretty - -mkdir -p "$_artifacts/macOS" -cp -R $(cat .bx_product_path) "$_artifacts/macOS/" +zip -ryq "$(cat .bx_product_path)" "$_artifacts/BXSwiftUtils-macoS.framework.zip" set -o pipefail; xcodebuild -scheme "BXSwiftUtils-iOS" -configuration "Release" clean build | xcpretty - -mkdir -p "$_artifacts/iOS" -cp -R $(cat .bx_product_path) "$_artifacts/iOS/" +zip -ryq "$(cat .bx_product_path)" "$_artifacts/BXSwiftUtils-iOS.framework.zip" # Delete git tag if already existing -git remote add origin-authenticated "https://${GH_TOKEN}@github.com/boinx/BXSwiftUtils.git" - -# if deleting the tag fails, that's ok -set +e -_git_tag="latest" -git tag -d ${_git_tag} -git push origin-authenticated :refs/tags/${_git_tag} -set -e +#git remote add origin-authenticated "https://${GH_TOKEN}@github.com/boinx/BXSwiftUtils.git" +# +## if deleting the tag fails, that's ok +#set +e +#_git_tag="travis" +#git tag -d ${_git_tag} +#git push origin-authenticated :refs/tags/${_git_tag} +#set -e # Create new "latest" tag -git tag -f -m "Travis tagged with ${_git_tag}" -a "${_git_tag}" -git push --tags origin-authenticated master +#git tag -f -m "Travis tagged with ${_git_tag}" -a "${_git_tag}" +#git push --tags origin-authenticated master From 6c89901c8673cac545551cc9e3225489b707783b Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Mon, 15 Oct 2018 14:37:52 +0200 Subject: [PATCH 10/17] Fix stupid zip command --- deploy.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy.sh b/deploy.sh index 64f25ad..a7603bd 100644 --- a/deploy.sh +++ b/deploy.sh @@ -7,10 +7,10 @@ rm -Rf "$_artifacts" mkdir "$_artifacts" set -o pipefail; xcodebuild -scheme "BXSwiftUtils-macOS" -configuration "Release" clean build | xcpretty -zip -ryq "$(cat .bx_product_path)" "$_artifacts/BXSwiftUtils-macoS.framework.zip" +zip -ryq "$_artifacts/BXSwiftUtils-macoS.framework.zip" "$(cat .bx_product_path)" set -o pipefail; xcodebuild -scheme "BXSwiftUtils-iOS" -configuration "Release" clean build | xcpretty -zip -ryq "$(cat .bx_product_path)" "$_artifacts/BXSwiftUtils-iOS.framework.zip" +zip -ryq "$_artifacts/BXSwiftUtils-iOS.framework.zip" "$(cat .bx_product_path)" # Delete git tag if already existing #git remote add origin-authenticated "https://${GH_TOKEN}@github.com/boinx/BXSwiftUtils.git" From 9b2a0c73644987da66634f346c3a436a584d5b62 Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Mon, 15 Oct 2018 14:49:37 +0200 Subject: [PATCH 11/17] Improve zip building --- .gitignore | 2 +- BXSwiftUtils.xcodeproj/project.pbxproj | 4 ++-- deploy.sh | 14 ++++++++++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 211170f..341609a 100644 --- a/.gitignore +++ b/.gitignore @@ -67,5 +67,5 @@ fastlane/screenshots fastlane/test_output .DS_Store -.bx_product_path +.bx_build_env Artifacts diff --git a/BXSwiftUtils.xcodeproj/project.pbxproj b/BXSwiftUtils.xcodeproj/project.pbxproj index 0a40a72..5a94a91 100644 --- a/BXSwiftUtils.xcodeproj/project.pbxproj +++ b/BXSwiftUtils.xcodeproj/project.pbxproj @@ -833,7 +833,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "echo \"Write product path to .bx_product_path\"\necho \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME\" > $SRCROOT/.bx_product_path\n"; + shellScript = "echo \"Write build details to .bx_build_env\"\necho \"BUILD_PRODUCTS_DIR=$BUILT_PRODUCTS_DIR PRODUCT_NAME=$FULL_PRODUCT_NAME SRCROOT=$SRCROOT\" > $SRCROOT/.bx_build_env\n"; showEnvVarsInLog = 0; }; 481A257921748DBE00FB8E9A /* Export Product Location */ = { @@ -852,7 +852,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "echo \"Write product path to .bx_product_path\"\necho \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME\" > $SRCROOT/.bx_product_path\n"; + shellScript = "echo \"Write build details to .bx_build_env\"\necho \"BUILD_PRODUCTS_DIR=$BUILT_PRODUCTS_DIR PRODUCT_NAME=$FULL_PRODUCT_NAME SRCROOT=$SRCROOT\" > $SRCROOT/.bx_build_env\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ diff --git a/deploy.sh b/deploy.sh index a7603bd..7d5d6b3 100644 --- a/deploy.sh +++ b/deploy.sh @@ -7,10 +7,20 @@ rm -Rf "$_artifacts" mkdir "$_artifacts" set -o pipefail; xcodebuild -scheme "BXSwiftUtils-macOS" -configuration "Release" clean build | xcpretty -zip -ryq "$_artifacts/BXSwiftUtils-macoS.framework.zip" "$(cat .bx_product_path)" + +source .bx_build_env + +pushd "$BUILD_PRODUCTS_DIR" +zip -ryq "$SRCROOT/$_artifacts/BXSwiftUtils-macoS.framework.zip" "$PRODUCT_NAME" +popd set -o pipefail; xcodebuild -scheme "BXSwiftUtils-iOS" -configuration "Release" clean build | xcpretty -zip -ryq "$_artifacts/BXSwiftUtils-iOS.framework.zip" "$(cat .bx_product_path)" + +source .bx_build_env + +pushd "$BUILD_PRODUCTS_DIR" +zip -ryq "$SRCROOT/$_artifacts/BXSwiftUtils-iOS.framework.zip" "$PRODUCT_NAME" +popd # Delete git tag if already existing #git remote add origin-authenticated "https://${GH_TOKEN}@github.com/boinx/BXSwiftUtils.git" From 08c1d932303ca8c47945cff63c1f06238f51ce11 Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Mon, 15 Oct 2018 15:06:42 +0200 Subject: [PATCH 12/17] Fix typo in macOS framework --- deploy.sh | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/deploy.sh b/deploy.sh index 7d5d6b3..8ef09d4 100644 --- a/deploy.sh +++ b/deploy.sh @@ -11,7 +11,7 @@ set -o pipefail; xcodebuild -scheme "BXSwiftUtils-macOS" -configuration "Release source .bx_build_env pushd "$BUILD_PRODUCTS_DIR" -zip -ryq "$SRCROOT/$_artifacts/BXSwiftUtils-macoS.framework.zip" "$PRODUCT_NAME" +zip -ryq "$SRCROOT/$_artifacts/BXSwiftUtils-macOS.framework.zip" "$PRODUCT_NAME" popd set -o pipefail; xcodebuild -scheme "BXSwiftUtils-iOS" -configuration "Release" clean build | xcpretty @@ -22,16 +22,3 @@ pushd "$BUILD_PRODUCTS_DIR" zip -ryq "$SRCROOT/$_artifacts/BXSwiftUtils-iOS.framework.zip" "$PRODUCT_NAME" popd -# Delete git tag if already existing -#git remote add origin-authenticated "https://${GH_TOKEN}@github.com/boinx/BXSwiftUtils.git" -# -## if deleting the tag fails, that's ok -#set +e -#_git_tag="travis" -#git tag -d ${_git_tag} -#git push origin-authenticated :refs/tags/${_git_tag} -#set -e - -# Create new "latest" tag -#git tag -f -m "Travis tagged with ${_git_tag}" -a "${_git_tag}" -#git push --tags origin-authenticated master From 4e750b6f20be70f8e57a392d57fd43b482c6d075 Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Mon, 15 Oct 2018 15:45:50 +0200 Subject: [PATCH 13/17] Build release on Xcode 9.4 This should allow older versions of Xcode to consume the framework --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4cd8f2b..fdd9e58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ matrix: env: SCHEME="BXSwiftUtils-macOS" DESTINATION="platform=macOS,arch=x86_64" - osx_image: xcode10 env: SCHEME="BXSwiftUtils-iOS" DESTINATION="platform=iOS Simulator,name=iPhone X,OS=12.0" - - osx_image: xcode10 + - osx_image: xcode9.4 env: CREATE_RELEASE="true" script: sh deploy.sh script: set -o pipefail; xcodebuild -scheme "$SCHEME" -destination "$DESTINATION" clean build test | xcpretty From e89f2fec5b3525e3390c005e5fdd27e75f914e61 Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Tue, 16 Oct 2018 06:04:17 +0200 Subject: [PATCH 14/17] deploy.sh: Build an iOS Simulator build instead of arm --- .travis.yml | 2 +- deploy.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fdd9e58..4cd8f2b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ matrix: env: SCHEME="BXSwiftUtils-macOS" DESTINATION="platform=macOS,arch=x86_64" - osx_image: xcode10 env: SCHEME="BXSwiftUtils-iOS" DESTINATION="platform=iOS Simulator,name=iPhone X,OS=12.0" - - osx_image: xcode9.4 + - osx_image: xcode10 env: CREATE_RELEASE="true" script: sh deploy.sh script: set -o pipefail; xcodebuild -scheme "$SCHEME" -destination "$DESTINATION" clean build test | xcpretty diff --git a/deploy.sh b/deploy.sh index 8ef09d4..796a779 100644 --- a/deploy.sh +++ b/deploy.sh @@ -14,7 +14,7 @@ pushd "$BUILD_PRODUCTS_DIR" zip -ryq "$SRCROOT/$_artifacts/BXSwiftUtils-macOS.framework.zip" "$PRODUCT_NAME" popd -set -o pipefail; xcodebuild -scheme "BXSwiftUtils-iOS" -configuration "Release" clean build | xcpretty +set -o pipefail; xcodebuild -scheme "BXSwiftUtils-iOS" -configuration "Release" -destination "platform=iOS Simulator,name=iPhone X,OS=latest" clean build | xcpretty source .bx_build_env From b669d73563567b1831b0a8e07d94d220fe4b85da Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Tue, 16 Oct 2018 07:13:18 +0200 Subject: [PATCH 15/17] Remove AppKit/UIKit related files that have been migrated to BXUIKit --- BXSwiftUtils.xcodeproj/project.pbxproj | 40 +- BXSwiftUtils/AppKit/BXMenuItem.swift | 41 - .../AppKit/NSEvent+ModifierKeys.swift | 70 -- .../AppKit/NSMenu+Concatenation.swift | 20 - .../AppKit/NSMenuItem+ConvenienceInit.swift | 38 - .../NSLocale+FMExtensions.swift | 0 BXSwiftUtils/UIKit/AVAudioPlayer+Play.swift | 37 - .../UIKit/NSArrayController+UIKit.swift | 830 ------------------ 8 files changed, 3 insertions(+), 1073 deletions(-) delete mode 100644 BXSwiftUtils/AppKit/BXMenuItem.swift delete mode 100644 BXSwiftUtils/AppKit/NSEvent+ModifierKeys.swift delete mode 100644 BXSwiftUtils/AppKit/NSMenu+Concatenation.swift delete mode 100644 BXSwiftUtils/AppKit/NSMenuItem+ConvenienceInit.swift rename BXSwiftUtils/{AppKit => Locale}/NSLocale+FMExtensions.swift (100%) delete mode 100644 BXSwiftUtils/UIKit/AVAudioPlayer+Play.swift delete mode 100644 BXSwiftUtils/UIKit/NSArrayController+UIKit.swift diff --git a/BXSwiftUtils.xcodeproj/project.pbxproj b/BXSwiftUtils.xcodeproj/project.pbxproj index 5a94a91..ad1100c 100644 --- a/BXSwiftUtils.xcodeproj/project.pbxproj +++ b/BXSwiftUtils.xcodeproj/project.pbxproj @@ -19,8 +19,6 @@ 484D0654205927F0003C6CA3 /* Array+AllEqual.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484D0652205927EC003C6CA3 /* Array+AllEqual.swift */; }; 484D065620592898003C6CA3 /* Array+AllEqualTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484D065520592898003C6CA3 /* Array+AllEqualTests.swift */; }; 484D065720592898003C6CA3 /* Array+AllEqualTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484D065520592898003C6CA3 /* Array+AllEqualTests.swift */; }; - 484FC80020E382A900AA6E3E /* NSMenuItem+ConvenienceInit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484FC7FF20E382A900AA6E3E /* NSMenuItem+ConvenienceInit.swift */; }; - 484FC80320E3845200AA6E3E /* NSMenu+Concatenation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484FC80220E3845200AA6E3E /* NSMenu+Concatenation.swift */; }; 4853E38A2056BDE400938B82 /* SynchronizedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4853E3892056BDE400938B82 /* SynchronizedTests.swift */; }; 4853E38B2056BDE400938B82 /* SynchronizedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4853E3892056BDE400938B82 /* SynchronizedTests.swift */; }; 4853E38D2056CF2C00938B82 /* BXReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4853E38C2056CF2C00938B82 /* BXReadWriteLock.swift */; }; @@ -47,7 +45,6 @@ 4875F27B20529A19009985EC /* Collection+SafeAccessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4875F2542051EF7F009985EC /* Collection+SafeAccessTests.swift */; }; 487A39CD2069174E007BEC66 /* TypedKVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487A39CC2069174E007BEC66 /* TypedKVO.swift */; }; 487A39CE2069174E007BEC66 /* TypedKVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487A39CC2069174E007BEC66 /* TypedKVO.swift */; }; - 487A39D120691E06007BEC66 /* NSArrayController+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487A39CF20691E05007BEC66 /* NSArrayController+UIKit.swift */; }; 487A39D220693A25007BEC66 /* KVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487A39C920690635007BEC66 /* KVO.swift */; }; 487A39D320693A25007BEC66 /* KVO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487A39C920690635007BEC66 /* KVO.swift */; }; 487E641E20A0A659007C4A8A /* Collection+Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487E641D20A0A659007C4A8A /* Collection+Flatten.swift */; }; @@ -105,7 +102,6 @@ 48DE068E20876E52000246CD /* Dictionary+EnumKeysTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DE068C20876E52000246CD /* Dictionary+EnumKeysTests.swift */; }; 48DF72F520697E1E00D224AA /* TypedKVOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DF72F420697E1E00D224AA /* TypedKVOTests.swift */; }; 48DF72F620697E1E00D224AA /* TypedKVOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DF72F420697E1E00D224AA /* TypedKVOTests.swift */; }; - 48E449D1205BBE81008CF3D5 /* BXMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48E449D0205BBE81008CF3D5 /* BXMenuItem.swift */; }; 48FF9BC720E116B100024C10 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48FF9BC620E116B100024C10 /* Weak.swift */; }; 48FF9BC820E116B100024C10 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48FF9BC620E116B100024C10 /* Weak.swift */; }; 48FF9BCA20E1174C00024C10 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48FF9BC920E1174C00024C10 /* Weak.swift */; }; @@ -148,9 +144,6 @@ D0859FD22158F7DD00F624C7 /* URL+Contents.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0859FD02158F7DD00F624C7 /* URL+Contents.swift */; }; D0859FD42158F7E200F624C7 /* URL+ExtendedAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0859FD32158F7E200F624C7 /* URL+ExtendedAttributes.swift */; }; D0859FD52158F7E200F624C7 /* URL+ExtendedAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0859FD32158F7E200F624C7 /* URL+ExtendedAttributes.swift */; }; - D0859FD72158F97200F624C7 /* NSEvent+ModifierKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0859FD62158F97200F624C7 /* NSEvent+ModifierKeys.swift */; }; - D0859FD92158FAF900F624C7 /* AVAudioPlayer+Play.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0859FD82158FAF900F624C7 /* AVAudioPlayer+Play.swift */; }; - D0859FDA2158FAF900F624C7 /* AVAudioPlayer+Play.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0859FD82158FAF900F624C7 /* AVAudioPlayer+Play.swift */; }; D0859FDC2158FBF100F624C7 /* CGSize+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0859FDB2158FBF100F624C7 /* CGSize+String.swift */; }; D0859FDD2158FBF100F624C7 /* CGSize+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0859FDB2158FBF100F624C7 /* CGSize+String.swift */; }; D0859FDF2158FC7400F624C7 /* NSLocale+FMExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0859FDE2158FC7400F624C7 /* NSLocale+FMExtensions.swift */; }; @@ -197,8 +190,6 @@ 484133732052D0C500DDB1E2 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 484D0652205927EC003C6CA3 /* Array+AllEqual.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+AllEqual.swift"; sourceTree = ""; }; 484D065520592898003C6CA3 /* Array+AllEqualTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+AllEqualTests.swift"; sourceTree = ""; }; - 484FC7FF20E382A900AA6E3E /* NSMenuItem+ConvenienceInit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSMenuItem+ConvenienceInit.swift"; sourceTree = ""; }; - 484FC80220E3845200AA6E3E /* NSMenu+Concatenation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSMenu+Concatenation.swift"; sourceTree = ""; }; 4853E3892056BDE400938B82 /* SynchronizedTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchronizedTests.swift; sourceTree = ""; }; 4853E38C2056CF2C00938B82 /* BXReadWriteLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BXReadWriteLock.swift; sourceTree = ""; }; 4853E38F2056D11500938B82 /* BXReadWriteLockTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BXReadWriteLockTests.swift; sourceTree = ""; }; @@ -219,7 +210,6 @@ 4875F267205299A6009985EC /* BXSwiftUtilsTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "BXSwiftUtilsTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 487A39C920690635007BEC66 /* KVO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVO.swift; sourceTree = ""; }; 487A39CC2069174E007BEC66 /* TypedKVO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypedKVO.swift; sourceTree = ""; }; - 487A39CF20691E05007BEC66 /* NSArrayController+UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSArrayController+UIKit.swift"; sourceTree = ""; }; 487E623020986792007C4A8A /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = SOURCE_ROOT; }; 487E62312098679A007C4A8A /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = SOURCE_ROOT; }; 487E641D20A0A659007C4A8A /* Collection+Flatten.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+Flatten.swift"; sourceTree = ""; }; @@ -248,7 +238,6 @@ 48DE068920876612000246CD /* Dictionary+EnumKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+EnumKeys.swift"; sourceTree = ""; }; 48DE068C20876E52000246CD /* Dictionary+EnumKeysTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+EnumKeysTests.swift"; sourceTree = ""; }; 48DF72F420697E1E00D224AA /* TypedKVOTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypedKVOTests.swift; sourceTree = ""; }; - 48E449D0205BBE81008CF3D5 /* BXMenuItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BXMenuItem.swift; sourceTree = ""; }; 48FF9BC620E116B100024C10 /* Weak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weak.swift; sourceTree = ""; }; 48FF9BC920E1174C00024C10 /* Weak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weak.swift; sourceTree = ""; }; D0149006213D566500A38870 /* KVO+propagateChanges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KVO+propagateChanges.swift"; sourceTree = ""; }; @@ -272,8 +261,6 @@ D06F462C209073D0000986B8 /* Collection+Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Collection+Codable.swift"; sourceTree = ""; }; D0859FD02158F7DD00F624C7 /* URL+Contents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+Contents.swift"; sourceTree = ""; }; D0859FD32158F7E200F624C7 /* URL+ExtendedAttributes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+ExtendedAttributes.swift"; sourceTree = ""; }; - D0859FD62158F97200F624C7 /* NSEvent+ModifierKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSEvent+ModifierKeys.swift"; sourceTree = ""; }; - D0859FD82158FAF900F624C7 /* AVAudioPlayer+Play.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVAudioPlayer+Play.swift"; sourceTree = ""; }; D0859FDB2158FBF100F624C7 /* CGSize+String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGSize+String.swift"; sourceTree = ""; }; D0859FDE2158FC7400F624C7 /* NSLocale+FMExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSLocale+FMExtensions.swift"; sourceTree = ""; }; D0891243215A762200403100 /* CGPoint+String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGPoint+String.swift"; sourceTree = ""; }; @@ -378,8 +365,7 @@ D0323501205C2923003D6CCB /* Math & Geometry */, 4875F2242051E9B7009985EC /* Collections */, D0323505205C2C3E003D6CCB /* Metal */, - 48E449CE205BBE55008CF3D5 /* AppKit */, - 48DF72ED2069440900D224AA /* UIKit */, + 48E449CE205BBE55008CF3D5 /* Locale */, 4875F20A2051E70E009985EC /* BXSwiftUtils.h */, 4875F20B2051E70E009985EC /* Info.plist */, ); @@ -517,15 +503,6 @@ path = Exceptions; sourceTree = ""; }; - 48DF72ED2069440900D224AA /* UIKit */ = { - isa = PBXGroup; - children = ( - 487A39CF20691E05007BEC66 /* NSArrayController+UIKit.swift */, - D0859FD82158FAF900F624C7 /* AVAudioPlayer+Play.swift */, - ); - path = UIKit; - sourceTree = ""; - }; 48DF72EE20697DCE00D224AA /* KVO */ = { isa = PBXGroup; children = ( @@ -537,16 +514,12 @@ path = KVO; sourceTree = ""; }; - 48E449CE205BBE55008CF3D5 /* AppKit */ = { + 48E449CE205BBE55008CF3D5 /* Locale */ = { isa = PBXGroup; children = ( - D0859FD62158F97200F624C7 /* NSEvent+ModifierKeys.swift */, - 48E449D0205BBE81008CF3D5 /* BXMenuItem.swift */, - 484FC7FF20E382A900AA6E3E /* NSMenuItem+ConvenienceInit.swift */, - 484FC80220E3845200AA6E3E /* NSMenu+Concatenation.swift */, D0859FDE2158FC7400F624C7 /* NSLocale+FMExtensions.swift */, ); - path = AppKit; + path = Locale; sourceTree = ""; }; D03234EC205BF93E003D6CCB /* Files */ = { @@ -865,9 +838,7 @@ D0DA3332213DA04A00994E89 /* DispatchGroup+Once.swift in Sources */, D0859FD42158F7E200F624C7 /* URL+ExtendedAttributes.swift in Sources */, D0891245215A762200403100 /* CGPoint+String.swift in Sources */, - D0859FD92158FAF900F624C7 /* AVAudioPlayer+Play.swift in Sources */, 4826E57E208F533400A5BA9B /* Array+Decodable.swift in Sources */, - 484FC80320E3845200AA6E3E /* NSMenu+Concatenation.swift in Sources */, 481BE6D32098362C00F12EDF /* Sequence+CompactMap.swift in Sources */, 48C248102088CE9400DC9317 /* NSAttributedString+Codable.swift in Sources */, D0149007213D566500A38870 /* KVO+propagateChanges.swift in Sources */, @@ -881,14 +852,12 @@ 4875F2262051E9D0009985EC /* Array+Concatenation.swift in Sources */, D08F44E021708CD90060FBAE /* String+Regex.swift in Sources */, D0859FDC2158FBF100F624C7 /* CGSize+String.swift in Sources */, - 484FC80020E382A900AA6E3E /* NSMenuItem+ConvenienceInit.swift in Sources */, 48C3DF6520E10DC300359288 /* TypedKVO+propagateChanges.swift in Sources */, 48AD6C9D2147FCC400225D9C /* String+UniqueByIncrementing.swift in Sources */, 487E641E20A0A659007C4A8A /* Collection+Flatten.swift in Sources */, D0859FDF2158FC7400F624C7 /* NSLocale+FMExtensions.swift in Sources */, D056D430205276ED00849EF2 /* DispatchQueue+Throttle.swift in Sources */, D046DF89216BAB59004331C3 /* String+DataDetection.swift in Sources */, - 48E449D1205BBE81008CF3D5 /* BXMenuItem.swift in Sources */, D056D43D205296A500849EF2 /* BXLogger+Domains.swift in Sources */, D0693F8A20AC1C4500B5893C /* UndoManager+Disabling.swift in Sources */, 487107EF20A9B8E000120024 /* BXAtomic.swift in Sources */, @@ -912,7 +881,6 @@ D032350D205C362A003D6CCB /* Date+Components.swift in Sources */, 484D0653205927EC003C6CA3 /* Array+AllEqual.swift in Sources */, D0C47CE420EE55DA003D3FA3 /* Number+Random.swift in Sources */, - D0859FD72158F97200F624C7 /* NSEvent+ModifierKeys.swift in Sources */, D056D431205276ED00849EF2 /* NSObject+Coalescing.swift in Sources */, 4853E38D2056CF2C00938B82 /* BXReadWriteLock.swift in Sources */, 48B6191E215E35DB00E6E037 /* OptionalType.swift in Sources */, @@ -987,8 +955,6 @@ 48B61918215E2C8000E6E037 /* BXCleanupPool.swift in Sources */, 487A39D320693A25007BEC66 /* KVO.swift in Sources */, D089124B215B723C00403100 /* CGRect+String.swift in Sources */, - D0859FDA2158FAF900F624C7 /* AVAudioPlayer+Play.swift in Sources */, - 487A39D120691E06007BEC66 /* NSArrayController+UIKit.swift in Sources */, 48AD6C9E2147FCC400225D9C /* String+UniqueByIncrementing.swift in Sources */, D0859FD22158F7DD00F624C7 /* URL+Contents.swift in Sources */, D0323504205C2C3B003D6CCB /* MTLTexture+Extensions.swift in Sources */, diff --git a/BXSwiftUtils/AppKit/BXMenuItem.swift b/BXSwiftUtils/AppKit/BXMenuItem.swift deleted file mode 100644 index cddbc27..0000000 --- a/BXSwiftUtils/AppKit/BXMenuItem.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// BXMenuItem.swift -// mimoLive -// -// Created by Stefan Fochler on 15.03.18. -// Copyright © 2018 Boinx Software Ltd. All rights reserved. -// - -import Cocoa - -/** - A NSMenuItem alternative (subclass, in fact) that, instead of taking a target and selector, uses a handler closure - that will be called with `representedObject` as arugment on selection. - This simplifies the workflow of presenting a list of homogenous items and using the selection result. - - Unfortunately, testing this class requires a run loop or even a complete application and is therefore not feasible. - */ -public class BXMenuItem: NSMenuItem -{ - private let handler: (T) -> () - - public init(title string: String, representedObject: T, selectionHandler: @escaping (T) -> ()) - { - self.handler = selectionHandler - - super.init(title: string, action: #selector(action(_:)), keyEquivalent: "") - - self.target = self - self.representedObject = representedObject - } - - required public init(coder decoder: NSCoder) - { - fatalError("init(coder:) has not been implemented") - } - - @objc private func action(_ sender: Any) - { - self.handler(self.representedObject as! T) - } -} diff --git a/BXSwiftUtils/AppKit/NSEvent+ModifierKeys.swift b/BXSwiftUtils/AppKit/NSEvent+ModifierKeys.swift deleted file mode 100644 index 027c19e..0000000 --- a/BXSwiftUtils/AppKit/NSEvent+ModifierKeys.swift +++ /dev/null @@ -1,70 +0,0 @@ -//********************************************************************************************************************** -// -// NSEvent+ModifierKeys.swift -// Adds convenience methods for checking modified keys -// Copyright ©2017 Peter Baumgartner. All rights reserved. -// -//********************************************************************************************************************** - - -import AppKit - - -//---------------------------------------------------------------------------------------------------------------------- - - -extension NSEvent -{ - /// Returns the keycode, useful for arrow keys of other function keys. - - public var key:Int - { - let str = charactersIgnoringModifiers!.utf16 - return Int(str[str.startIndex]) - } - - /// Returns true if the command key is down. - - public var isCommandDown:Bool - { - return self.modifierFlags.contains(.command) - } - - /// Returns true if the option key is down. - - public var isOptionDown:Bool - { - return self.modifierFlags.contains(.option) - } - - /// Returns true if the control key is down. - - public var isControlDown:Bool - { - return self.modifierFlags.contains(.control) - } - - /// Returns true if the shift key is down. - - public var isShiftDown:Bool - { - return self.modifierFlags.contains(.shift) - } - - /// Returns true if the capslock key is down. - - public var isCapsLockDown:Bool - { - return self.modifierFlags.contains(.capsLock) - } - - /// Returns true if no modifier keys are pressed. - - public var isNoModifierDown:Bool - { - return self.modifierFlags.intersection([.command,.option,.control,.shift,.capsLock]).isEmpty - } -} - - -//---------------------------------------------------------------------------------------------------------------------- diff --git a/BXSwiftUtils/AppKit/NSMenu+Concatenation.swift b/BXSwiftUtils/AppKit/NSMenu+Concatenation.swift deleted file mode 100644 index 326efa6..0000000 --- a/BXSwiftUtils/AppKit/NSMenu+Concatenation.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// NSMenu.swift -// BXSwiftUtils-macOS -// -// Created by Stefan Fochler on 27.06.18. -// Copyright © 2018 Boinx Software Ltd. & Imagine GbR. All rights reserved. -// - -import AppKit - -extension NSMenu -{ - /** - Appends a menu item to a menu using a conventient notation. - */ - public static func +=(menu: NSMenu, item: NSMenuItem) - { - menu.addItem(item) - } -} diff --git a/BXSwiftUtils/AppKit/NSMenuItem+ConvenienceInit.swift b/BXSwiftUtils/AppKit/NSMenuItem+ConvenienceInit.swift deleted file mode 100644 index 9886a89..0000000 --- a/BXSwiftUtils/AppKit/NSMenuItem+ConvenienceInit.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// NSMenuItem+EnumCaseInit.swift -// BXSwiftUtils -// -// Created by Stefan Fochler on 27.06.18. -// Copyright © 2018 Boinx Software Ltd. & Imagine GbR. All rights reserved. -// - -import AppKit - -extension NSMenuItem -{ - /** - Convenience initializer that creates a menu item with a given `title` and `value` which will be used as the item's - `representedObject`. - - - parameter title: The item's displayable title. - - parameter value: The value that is represented by this item. - */ - public convenience init(title: String, value: Any, indentationLevel: Int = 0, enabled: Bool = true) - { - self.init(title: title, action: nil, keyEquivalent: "") - self.representedObject = value - self.indentationLevel = indentationLevel - self.isEnabled = enabled - } - - /** - Convenience initializer that creates a menu item with the given `value` used as the item's `representedObject`. - The item's `title` will be set to `value`'s `rawValue`, which must be a `String`. - - - parameter value: The value that is represented by this item and the raw value of which must be a String. - */ - public convenience init(for value: Value) where Value: RawRepresentable, Value.RawValue == String - { - self.init(title: value.rawValue, value: value) - } -} diff --git a/BXSwiftUtils/AppKit/NSLocale+FMExtensions.swift b/BXSwiftUtils/Locale/NSLocale+FMExtensions.swift similarity index 100% rename from BXSwiftUtils/AppKit/NSLocale+FMExtensions.swift rename to BXSwiftUtils/Locale/NSLocale+FMExtensions.swift diff --git a/BXSwiftUtils/UIKit/AVAudioPlayer+Play.swift b/BXSwiftUtils/UIKit/AVAudioPlayer+Play.swift deleted file mode 100644 index 89223b1..0000000 --- a/BXSwiftUtils/UIKit/AVAudioPlayer+Play.swift +++ /dev/null @@ -1,37 +0,0 @@ -//********************************************************************************************************************** -// -// AVAudioPlayer+Play.swift -// Adds simple one-liner audio playback -// Copyright ©2017 Peter Baumgartner. All rights reserved. -// -//********************************************************************************************************************** - - -import AVFoundation - - -//---------------------------------------------------------------------------------------------------------------------- - - -public extension AVAudioPlayer -{ - /// One-liner method to asynchronously play a named audio file - /// - parameter name: The name of the audio file - /// - parameter bundle: The bundle containing the audio file - - public static func playAudio(named name:String, in bundle:Bundle = Bundle.main) - { - if let url = bundle.url(forResource:name,withExtension:nil) - { -// try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback) -// try? AVAudioSession.sharedInstance().setActive(true) - - let audioPlayer = try? AVAudioPlayer(contentsOf:url) - audioPlayer?.prepareToPlay() - audioPlayer?.play() - } - } -} - - -//---------------------------------------------------------------------------------------------------------------------- diff --git a/BXSwiftUtils/UIKit/NSArrayController+UIKit.swift b/BXSwiftUtils/UIKit/NSArrayController+UIKit.swift deleted file mode 100644 index cd23554..0000000 --- a/BXSwiftUtils/UIKit/NSArrayController+UIKit.swift +++ /dev/null @@ -1,830 +0,0 @@ -//********************************************************************************************************************** -// -// NSArrayController+UIKit.swift -// Missing NSArrayController class for UIKit -// Copyright ©2017 Peter Baumgartner. All rights reserved. -// -//********************************************************************************************************************** - - -import Foundation - - -//---------------------------------------------------------------------------------------------------------------------- - - -// Since NSArrayController is part of AppKit and not Foundation (how stupid is that?) we need to supply -// this class ourself for iOS projects. Many thanks to Cocotron for details on the implementation: -// https://github.com/toaster/hg-cocotron/blob/master/AppKit/NSController/NSArrayController.m - -#if os(iOS) - - -//---------------------------------------------------------------------------------------------------------------------- - - -public let NSNoSelectionMarker = "NSNoSelectionMarker" as AnyObject -public let NSMultipleValuesMarker = "NSMultipleValuesMarker" as AnyObject -public let NSNotApplicableMarker = "NSNotApplicableMarker" as AnyObject - -public func NSIsControllerMarker(_ value:Any?) -> Bool -{ - let object = value as AnyObject - - return object === NSNoSelectionMarker || - object === NSMultipleValuesMarker || - object === NSNotApplicableMarker -} - -public func NSIsMultipleValuesMarker(_ value:Any?) -> Bool -{ - let object = value as AnyObject - return object === NSMultipleValuesMarker -} - -public func NSIsNoSelectionMarker(_ value:Any?) -> Bool -{ - let object = value as AnyObject - return object === NSNoSelectionMarker -} - -public func NSIsNotApplicableMarker(_ value:Any?) -> Bool -{ - let object = value as AnyObject - return object === NSNotApplicableMarker -} - - - -//---------------------------------------------------------------------------------------------------------------------- - - -public struct NSBindingName : RawRepresentable,Equatable,Hashable -{ - public let rawValue:String - - public init(_ rawValue:String) - { - self.rawValue = rawValue - } - - public init(rawValue:String) - { - self.rawValue = rawValue - } - - public var hashValue:Int - { - return self.rawValue.hashValue - } - - public static func ==(lhs:NSBindingName,rhs:NSBindingName) -> Bool - { - return lhs.rawValue == rhs.rawValue - } - - public static let contentArray = NSBindingName("contentArray") -} - - -//---------------------------------------------------------------------------------------------------------------------- - - -// This is a stripped down version of NSArrayController, that contains just the features that are needed in FotoMagico - -open class NSArrayController : NSObject -{ - - -//---------------------------------------------------------------------------------------------------------------------- - - - // Create the controller and a selection proxy object - - override public init() - { - super.init() - _selection = _NSSelectionProxy(controller:self) - } - - -//---------------------------------------------------------------------------------------------------------------------- - - - // Define KVO dependencies - - override open class func keyPathsForValuesAffectingValue(forKey key:String) -> Set - { - if key == "contentArray" - { - return Set(["content"]) - } - else if key == "selection" - { - return Set(["content","contentArray","selectionIndexes"]) - } - else if key == "selectionIndex" - { - return Set(["content","contentArray","selectionIndexes","selection"]) - } - else if key == "selectedObjects" - { - return Set(["content","contentArray","selectionIndexes","selection"]) - } - else if key == "canRemove" - { - return Set(["selectionIndexes"]) - } - else if key == "canSelectNext" - { - return Set(["selectionIndexes"]) - } - else if key == "canSelectPrevious" - { - return Set(["selectionIndexes"]) - } - - return super.keyPathsForValuesAffectingValue(forKey:key) - } - - -//---------------------------------------------------------------------------------------------------------------------- - - - // MARK: - // MARK: Content - - - /// This property can be used for programmatically setting the controllers 'contentArray' - - @objc dynamic open var content:Any? - { - set - { - if let array = content as? [Any] - { - self.contentArray = NSMutableArray(array:array) - self.rearrangeObjects() - } - } - - get - { - return self.contentArray - } - } - - /// The type of objects stored in the array - - open var objectClass:Swift.AnyClass! - - -//---------------------------------------------------------------------------------------------------------------------- - - - open func bind(_ binding:NSBindingName,to observable:Any,withKeyPath keyPath:String,options:[String:Any]?=nil) - { - if binding.rawValue == "contentArray" - { - if let object = observable as? NSObject - { - self.bindingObject = object - self.bindingKeyPath = keyPath - - self.updateContentArray() - - let keypathComponents = keyPath.components(separatedBy:".") - let keyPathPrefix = keypathComponents.first! -// let keyPathSuffix = keypathComponents.last! - - self.contentArrayObserver = KVO(object:object,keyPath:keyPathPrefix,options:[.initial,.new]) - { - [weak self] _,_ in - self?.updateContentArray() - } - } - } - } - - - open func unbind(_ binding:NSBindingName) - { - if binding.rawValue == "contentArray" - { - self.contentArray = nil - self.contentArrayObserver = nil - } - } - - - internal weak var bindingObject:NSObject? = nil - - internal var bindingKeyPath:String? = nil - - internal var contentArrayObserver:KVO? = nil - - -//---------------------------------------------------------------------------------------------------------------------- - - - // In case of @unionOfArrays, we'll need to observe each subarray - - internal func observeUnionOfArrays() - { - guard let object = self.bindingObject else { return } - guard let keyPath = bindingKeyPath else { return } - guard keyPath.contains("@unionOfArrays") else { return } - - let keypathComponents = keyPath.components(separatedBy:".") - guard let keyPathPrefix = keypathComponents.first else { return } - guard let keyPathSuffix = keypathComponents.last else { return } - guard let subObjects = object.value(forKeyPath:keyPathPrefix) as? [NSObject] else { return } - - self.unionOfArrayObservers = [] - - for subObject in subObjects - { - self.unionOfArrayObservers += KVO(object:subObject,keyPath:keyPathSuffix,options:.new) - { - [weak self] _,_ in - self?.updateContentArray() - } - } - } - - /// An list of KVO observers that watch the subarrays for changes - - internal var unionOfArrayObservers:[KVO] = [] - - -//---------------------------------------------------------------------------------------------------------------------- - - - /// This method is called whenever KVO detects a change in the data model. It then copies the array from - /// the data model to the private property 'contentArray', which will in turn to copied to 'arrangedObjects'. - - internal func updateContentArray() - { - if let object = self.bindingObject, let keyPath = bindingKeyPath - { - let swiftArray = object.value(forKeyPath:keyPath) // Returns Swift array type [Any] - let array = swiftArray as! NSArray // which can only be converted to NSArray, not NSMutableArray! - self.contentArray = NSMutableArray(array:array) // so converting to NSMutableArray takes an extra step! - - self.rearrangeObjects() - } - } - - - /// The contentArray represents the "input" side of the NSArrayController - - internal var contentArray:NSMutableArray? = nil - - -//---------------------------------------------------------------------------------------------------------------------- - - - // MARK: - // MARK: Arranging - - - open func rearrangeObjects() - { - if let objects = self.contentArray as? [Any] - { - // Save current selection - - let savedSelectedObjects = self.selectedObjects ?? [] - let selection = (_selection as? _NSSelectionProxy) - - // Prepare for changing the array - - self.willChangeValue(forKey:"arrangedObjects") - selection?.controllerWillChangeObjects() - - // Update the arrangedObjects array - - self._arrangedObjects = NSMutableArray(array:self.arrange(objects)) - - // In case of @unionOfArrays, we'll need to observe each subarray - - self.observeUnionOfArrays() - - // Restore the selection - - if preservesSelection - { - self.setSelectedObjects(savedSelectedObjects) - } - - // Notify others that arrangedObjects has changed - - selection?.controllerDidChangeObjects() - self.didChangeValue(forKey:"arrangedObjects") - } - } - - - /// This method filters and sorts the contentArray to produce the output, which is stored in arrangedObjects. - /// This class doesn't do any filtering or sorting, but subclasses can override this method. - - open func arrange(_ objects:[Any]) -> [Any] - { - return objects - } - - - /// arrangedObjects is the "output" of the NSArrayController. - - @objc open dynamic var arrangedObjects:NSMutableArray - { - return _arrangedObjects - } - - - /// Internal storage for the arrangedObjects - - internal var _arrangedObjects = NSMutableArray() - - -//---------------------------------------------------------------------------------------------------------------------- - - - // MARK: - // MARK: Selecting - - - open var avoidsEmptySelection = false - open var preservesSelection = true - open var alwaysUsesMultipleValuesMarker = false - - -//---------------------------------------------------------------------------------------------------------------------- - - - /// The index of the first selected object. If nothing is selected -1 will be returned. - - open var selectionIndex:Int - { - set - { - self.setSelectionIndexes(IndexSet(integer:newValue)) - } - - get - { - return self._selectionIndexes.first ?? -1 - } - } - - -//---------------------------------------------------------------------------------------------------------------------- - - - @discardableResult open func setSelectionIndexes(_ inIndexes:IndexSet) -> Bool - { - var indexes = inIndexes - - // If we're supposed to avoid an empty selection, then simply select the first object. - - if self.avoidsEmptySelection && indexes.count==0 && _arrangedObjects.count>0 - { - indexes = IndexSet(integer:0) - } - - // Bail out if the selection doesn't change. - - if _selectionIndexes == indexes - { - return false - } - - let selection = (_selection as? _NSSelectionProxy) - - self.willChangeValue(forKey:"selectionIndexes") - selection?.controllerWillChangeObjects() - self._selectionIndexes = indexes - selection?.controllerDidChangeObjects() - self.didChangeValue(forKey:"selectionIndexes") - - return true - } - - @objc open dynamic var selectionIndexes:IndexSet - { - return self._selectionIndexes - } - - internal var _selectionIndexes:IndexSet = IndexSet() - - -//---------------------------------------------------------------------------------------------------------------------- - - - // Sets the selectedObjects - - @discardableResult open func setSelectedObjects(_ selectedObjects:[Any]) -> Bool - { - var indexes = IndexSet() - - for object in selectedObjects - { - let i = _arrangedObjects.indexOfObjectIdentical(to:object) - - if i != NSNotFound - { - indexes.insert(i) - } - } - - return self.setSelectionIndexes(indexes) - } - - // Returns the selectedObjects. Please note that this algorithm tries to avoid exceptions if _selectionIndexes - // is out-dated and contains indexes that are not available in the _arrangedObjects array anymore. - - @objc open dynamic var selectedObjects:[Any]! - { -// return _arrangedObjects.objects(at:_selectionIndexes) - -// return _selectionIndexes.map -// { -// let i = $0 -// return i<_arrangedObjects.count ? _arrangedObjects[i] : 0 -// } - - var objects:[Any] = [] - - for i in _selectionIndexes - { - if i<_arrangedObjects.count - { - objects += _arrangedObjects[i] - } - } - - return objects - } - - -//---------------------------------------------------------------------------------------------------------------------- - - - @discardableResult open func addSelectedObjects(_ objects:[Any]) -> Bool - { - guard objects.count > 0 else { return false } - let selection = (_selection as? _NSSelectionProxy) - - self.willChangeValue(forKey:"selectionIndexes") - selection?.controllerWillChangeObjects() - - for object in objects - { - let i = _arrangedObjects.indexOfObjectIdentical(to:object) - - if i != NSNotFound - { - self._selectionIndexes.insert(i) - } - } - - selection?.controllerDidChangeObjects() - self.didChangeValue(forKey:"selectionIndexes") - - return objects.count > 0 - } - - - @discardableResult open func removeSelectedObjects(_ objects:[Any]) -> Bool - { - guard objects.count > 0 else { return false } - let selection = (_selection as? _NSSelectionProxy) - var didRemove = false - - self.willChangeValue(forKey:"selectionIndexes") - selection?.controllerWillChangeObjects() - - for object in objects - { - let i = _arrangedObjects.indexOfObjectIdentical(to:object) - - if i != NSNotFound - { - self._selectionIndexes.remove(i) - didRemove = true - } - } - - selection?.controllerDidChangeObjects() - self.didChangeValue(forKey:"selectionIndexes") - - return didRemove - } - - -//---------------------------------------------------------------------------------------------------------------------- - - - /// The selection proxy. Send KVC messages to this proxy to target all selectedObjects - - @objc open dynamic var selection:Any - { - return _selection - } - - internal var _selection:Any = 0 -} - - -//---------------------------------------------------------------------------------------------------------------------- - - -// MARK: - -internal struct _NSObserverInfo -{ - weak var observer:NSObject? = nil - var keyPath:String - var options:NSKeyValueObservingOptions - var context:UnsafeMutableRawPointer? = nil -} - - -//---------------------------------------------------------------------------------------------------------------------- - - -// MARK: - -/// Helper class that acts as a proxy for the selection of the NSArrayController. You can bind against this object. - -internal class _NSSelectionProxy : NSObject -{ - internal weak var arrayController:NSArrayController? - internal var observers:[_NSObserverInfo] = [] - internal var objectProxies:[_NSObjectProxy] = [] - - - init(controller:NSArrayController) - { - arrayController = controller - super.init() - } - - - // Sets the value on all selectedObjects of the owning controller. - - override func setValue(_ value:Any?,forKeyPath keyPath:String) - { - guard let selectedObjects = arrayController?.selectedObjects as? [NSObject] else { return } - - for object in selectedObjects - { - object.setValue(value,forKeyPath:keyPath) - } - } - - - override func setValue(_ value:Any?,forKey key:String) - { - guard let selectedObjects = arrayController?.selectedObjects as? [NSObject] else { return } - - for object in selectedObjects - { - object.setValue(value,forKey:key) - } - } - - - /// Returns the (common) value of all selected objects. If there is no common value then NSMultipleValuesMarker - /// will be returned instead. - - override func value(forKeyPath keyPath:String) -> Any? - { - guard let controller = arrayController - else { return NSNoSelectionMarker } - - guard let selectedObjects = controller.selectedObjects as? [NSObject] - else { return NSNoSelectionMarker } - - let alwaysUsesMultipleValuesMarker = controller.alwaysUsesMultipleValuesMarker - var uniqueValue:Any? = nil - - for object in selectedObjects - { - let value = object.value(forKeyPath:keyPath) - - if uniqueValue != nil && value != nil - { - if alwaysUsesMultipleValuesMarker - { - return NSMultipleValuesMarker - } - else if !isEqual(uniqueValue!,value!) - { - return NSMultipleValuesMarker - } - } - else - { - uniqueValue = value - } - } - - return uniqueValue - } - - - override func value(forKey key:String) -> Any? - { - guard let controller = arrayController - else { return NSNoSelectionMarker } - - guard let selectedObjects = controller.selectedObjects as? [NSObject] - else { return NSNoSelectionMarker } - - let alwaysUsesMultipleValuesMarker = controller.alwaysUsesMultipleValuesMarker - var uniqueValue:Any? = nil - - for object in selectedObjects - { - let value = object.value(forKey:key) - - if uniqueValue != nil && value != nil - { - if alwaysUsesMultipleValuesMarker - { - return NSMultipleValuesMarker - } - else if !isEqual(uniqueValue!,value!) - { - return NSMultipleValuesMarker - } - } - else - { - uniqueValue = value - } - } - - return uniqueValue - } - - - internal func isEqual(_ a:Any,_ b:Any) -> Bool - { - guard let objectA = a as? NSObject else { return false } - guard let objectB = b as? NSObject else { return false } - return objectA.isEqual(objectB) - -// let TypeA = type(of:a) -// let TypeB = type(of:b) -// -// guard TypeA == TypeB else { return false } -// guard let a = a as? TypeA.self, let b = b as? TypeA.self else { return false } -// return a == b - } - - - override func addObserver(_ observer:NSObject,forKeyPath keyPath:String,options:NSKeyValueObservingOptions=[],context:UnsafeMutableRawPointer?) - { - self.stopObservingSelectedObjects() - let info = _NSObserverInfo(observer:observer,keyPath:keyPath,options:options,context:context) - self.observers.append(info) - self.startObservingSelectedObjects() - } - - override func removeObserver(_ observer:NSObject,forKeyPath keyPath:String) - { - self.stopObservingSelectedObjects() - - if let index = self.observers.index(where:{ $0.observer===observer && $0.keyPath==keyPath }) - { - self.observers.remove(at:index) - } - - self.startObservingSelectedObjects() - } - - // Whenever the controller changes the arrangedObjects or selectedObjects, KVO observing must be temporarily - // stopped and then resumed once mutating the array has concluded. - - private var controllerChangeCount = 0 - - public func controllerWillChangeObjects() - { - if controllerChangeCount == 0 - { - self.stopObservingSelectedObjects() - } - - controllerChangeCount += 1 - } - - public func controllerDidChangeObjects() - { - controllerChangeCount -= 1 - - if controllerChangeCount == 0 - { - self.startObservingSelectedObjects() - } - } - - // Start KVO observing all selected objects - - func startObservingSelectedObjects() - { - guard let selectedObjects = arrayController?.selectedObjects as? [NSObject] else { return } - let keyPaths = self.observers.map { $0.keyPath } - self.objectProxies.removeAll() - - for object in selectedObjects - { - for keyPath in keyPaths - { - let proxy = _NSObjectProxy(owner:self,object:object,keyPath:keyPath,options:[.old,.new],context:nil) - objectProxies.append(proxy) - } - } - } - - // Stop KVO observing - - func stopObservingSelectedObjects() - { - self.objectProxies.removeAll() - } - - // Any KVO notifications received by a _NSObjectProxy will be routed to here. We now need to pass on this - // notification to all observers interested in this keyPath. - - override func observeValue(forKeyPath keyPath:String?,of object:Any?,change:[NSKeyValueChangeKey:Any]?,context:UnsafeMutableRawPointer?) - { - let targets = self.observers.filter { $0.keyPath == keyPath } - - for info in targets - { - let observer = info.observer?.value(forKey:"observer") as? NSObject - observer?.observeValue(forKeyPath:keyPath,of:self,change:change,context:info.context) - } - -// guard let change = change else { return } -// guard let prior = change[NSKeyValueChangeKey.notificationIsPriorKey] as? Bool else { return } -// guard let keyPath = keyPath else { return } -// -// if prior -// { -// self.willChangeValue(forKey:keyPath) -// } -// else -// { -// self.didChangeValue(forKey:keyPath) -// } - } - -} - - -//---------------------------------------------------------------------------------------------------------------------- - - -// MARK: - -/// Helper class that observes a single keypath of an object and passes any KVO notifications to the owner of this proxy - -internal class _NSObjectProxy : NSObject -{ - weak var owner:NSObject? - weak var object:NSObject? - var keyPath:String - - init(owner:NSObject,object:NSObject,keyPath:String,options:NSKeyValueObservingOptions=[],context:UnsafeMutableRawPointer?) - { - self.owner = owner - self.object = object - self.keyPath = keyPath - - super.init() - - object.addObserver(self,forKeyPath:keyPath,options:options,context:context) - } - - deinit - { - object?.removeObserver(self,forKeyPath:keyPath) - } - - override func observeValue(forKeyPath keyPath:String?,of object:Any?,change:[NSKeyValueChangeKey:Any]?,context:UnsafeMutableRawPointer?) - { - self.owner?.observeValue(forKeyPath:keyPath,of:object,change:change,context:context) - } -} - - -//---------------------------------------------------------------------------------------------------------------------- - - -#endif - - - From ce33203e938abbe86c67ffab64ffb40b71d5839b Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Tue, 16 Oct 2018 08:36:34 +0200 Subject: [PATCH 16/17] Add NSIsControllerMarker to KVO file --- BXSwiftUtils/KVO/KVO.swift | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/BXSwiftUtils/KVO/KVO.swift b/BXSwiftUtils/KVO/KVO.swift index 63a4c7c..13de9ad 100644 --- a/BXSwiftUtils/KVO/KVO.swift +++ b/BXSwiftUtils/KVO/KVO.swift @@ -9,11 +9,45 @@ import Foundation -// Importing AppKit is needed for the NSIsControllerMarker() function. On iOS BXSwiftUtils provides its own -// implementation of this function. +// Importing AppKit is needed for the NSIsControllerMarker() function. For iOS, provide our own implementation of this function. #if os(macOS) + import AppKit + +#elseif os(iOS) + +public let NSNoSelectionMarker = "NSNoSelectionMarker" as AnyObject +public let NSMultipleValuesMarker = "NSMultipleValuesMarker" as AnyObject +public let NSNotApplicableMarker = "NSNotApplicableMarker" as AnyObject + +public func NSIsControllerMarker(_ value:Any?) -> Bool +{ + let object = value as AnyObject + + return object === NSNoSelectionMarker || + object === NSMultipleValuesMarker || + object === NSNotApplicableMarker +} + +public func NSIsMultipleValuesMarker(_ value:Any?) -> Bool +{ + let object = value as AnyObject + return object === NSMultipleValuesMarker +} + +public func NSIsNoSelectionMarker(_ value:Any?) -> Bool +{ + let object = value as AnyObject + return object === NSNoSelectionMarker +} + +public func NSIsNotApplicableMarker(_ value:Any?) -> Bool +{ + let object = value as AnyObject + return object === NSNotApplicableMarker +} + #endif From 12b680945da68214df816d897dba372dc47d69f4 Mon Sep 17 00:00:00 2001 From: Stefan Fochler Date: Tue, 16 Oct 2018 10:15:05 +0200 Subject: [PATCH 17/17] Add CGPath+RoundedRect & CACornerMask+Convenience --- BXSwiftUtils.xcodeproj/project.pbxproj | 20 +++++ .../CACornerMask+Convenience.swift | 51 +++++++++++++ .../Math & Geometry/CGPath+RoundedRect.swift | 73 +++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 BXSwiftUtils/Math & Geometry/CACornerMask+Convenience.swift create mode 100644 BXSwiftUtils/Math & Geometry/CGPath+RoundedRect.swift diff --git a/BXSwiftUtils.xcodeproj/project.pbxproj b/BXSwiftUtils.xcodeproj/project.pbxproj index ad1100c..1237e84 100644 --- a/BXSwiftUtils.xcodeproj/project.pbxproj +++ b/BXSwiftUtils.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 481A25BD2175D36000FB8E9A /* CGPath+RoundedRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 481A25BC2175D36000FB8E9A /* CGPath+RoundedRect.swift */; }; + 481A25BE2175D36000FB8E9A /* CGPath+RoundedRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 481A25BC2175D36000FB8E9A /* CGPath+RoundedRect.swift */; }; + 481A25C02175D38700FB8E9A /* CACornerMask+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 481A25BF2175D38700FB8E9A /* CACornerMask+Convenience.swift */; }; + 481A25C12175D38700FB8E9A /* CACornerMask+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 481A25BF2175D38700FB8E9A /* CACornerMask+Convenience.swift */; }; 481BE6D32098362C00F12EDF /* Sequence+CompactMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 481BE6D22098362C00F12EDF /* Sequence+CompactMap.swift */; }; 481BE6D42098362C00F12EDF /* Sequence+CompactMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 481BE6D22098362C00F12EDF /* Sequence+CompactMap.swift */; }; 481BE6D62098368E00F12EDF /* Sequence+CompactMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 481BE6D52098368E00F12EDF /* Sequence+CompactMap.swift */; }; @@ -183,6 +187,8 @@ /* Begin PBXFileReference section */ 481A257B2174939600FB8E9A /* deploy.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = deploy.sh; sourceTree = ""; }; + 481A25BC2175D36000FB8E9A /* CGPath+RoundedRect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGPath+RoundedRect.swift"; sourceTree = ""; }; + 481A25BF2175D38700FB8E9A /* CACornerMask+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CACornerMask+Convenience.swift"; sourceTree = ""; }; 481BE6D22098362C00F12EDF /* Sequence+CompactMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+CompactMap.swift"; sourceTree = ""; }; 481BE6D52098368E00F12EDF /* Sequence+CompactMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+CompactMap.swift"; sourceTree = ""; }; 4826E57D208F533400A5BA9B /* Array+Decodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Decodable.swift"; sourceTree = ""; }; @@ -319,6 +325,13 @@ name = Config; sourceTree = ""; }; + 481A25BB2175D33400FB8E9A /* Core Animation */ = { + isa = PBXGroup; + children = ( + ); + path = "Core Animation"; + sourceTree = ""; + }; 4853E3882056BDB500938B82 /* Threading */ = { isa = PBXGroup; children = ( @@ -352,6 +365,7 @@ 4875F2092051E70E009985EC /* BXSwiftUtils */ = { isa = PBXGroup; children = ( + 481A25BB2175D33400FB8E9A /* Core Animation */, 48A971DF2159022D00215F9F /* Enums */, D056D43A2052968A00849EF2 /* Logging */, D056D43220527B7500849EF2 /* Threading */, @@ -553,6 +567,8 @@ D0891243215A762200403100 /* CGPoint+String.swift */, D0859FDB2158FBF100F624C7 /* CGSize+String.swift */, D089124A215B723C00403100 /* CGRect+String.swift */, + 481A25BC2175D36000FB8E9A /* CGPath+RoundedRect.swift */, + 481A25BF2175D38700FB8E9A /* CACornerMask+Convenience.swift */, ); path = "Math & Geometry"; sourceTree = ""; @@ -844,6 +860,7 @@ D0149007213D566500A38870 /* KVO+propagateChanges.swift in Sources */, D0323503205C2C3B003D6CCB /* MTLTexture+Extensions.swift in Sources */, D03234FF205C2919003D6CCB /* CGPoint+Operators.swift in Sources */, + 481A25C02175D38700FB8E9A /* CACornerMask+Convenience.swift in Sources */, D03F13B820F36D57004C8306 /* MTLComputeCommandEncoder+Extensions.swift in Sources */, D0EA52DA20F8992300A0EF6D /* MTKTextureLoader+Extensions.swift in Sources */, 48A971DA2158F75800215F9F /* Equatable+IsContainedIn.swift in Sources */, @@ -883,6 +900,7 @@ D0C47CE420EE55DA003D3FA3 /* Number+Random.swift in Sources */, D056D431205276ED00849EF2 /* NSObject+Coalescing.swift in Sources */, 4853E38D2056CF2C00938B82 /* BXReadWriteLock.swift in Sources */, + 481A25BD2175D36000FB8E9A /* CGPath+RoundedRect.swift in Sources */, 48B6191E215E35DB00E6E037 /* OptionalType.swift in Sources */, D056D43E205296A500849EF2 /* BXLogger.swift in Sources */, 48DA81D0206BCD92009D1E6C /* Collection+Pluck.swift in Sources */, @@ -932,6 +950,7 @@ D03F13B920F36D57004C8306 /* MTLComputeCommandEncoder+Extensions.swift in Sources */, 48FF9BC820E116B100024C10 /* Weak.swift in Sources */, 48A971DB2158F75800215F9F /* Equatable+IsContainedIn.swift in Sources */, + 481A25C12175D38700FB8E9A /* CACornerMask+Convenience.swift in Sources */, 4875F276205299F7009985EC /* Array+Concatenation.swift in Sources */, 481BE6D42098362C00F12EDF /* Sequence+CompactMap.swift in Sources */, D0149008213D566500A38870 /* KVO+propagateChanges.swift in Sources */, @@ -971,6 +990,7 @@ D0606C9520A5CFB50090D981 /* BXInstanceInfoMixin.swift in Sources */, 485E0CF120B580CD001F2247 /* DispatchQueue+Main.swift in Sources */, 489E56AD2052CC800071BBF1 /* Synchronized.swift in Sources */, + 481A25BE2175D36000FB8E9A /* CGPath+RoundedRect.swift in Sources */, 48C248112088CE9400DC9317 /* NSAttributedString+Codable.swift in Sources */, D0323500205C2919003D6CCB /* CGPoint+Operators.swift in Sources */, D0DA3333213DA04A00994E89 /* DispatchGroup+Once.swift in Sources */, diff --git a/BXSwiftUtils/Math & Geometry/CACornerMask+Convenience.swift b/BXSwiftUtils/Math & Geometry/CACornerMask+Convenience.swift new file mode 100644 index 0000000..7dcff8d --- /dev/null +++ b/BXSwiftUtils/Math & Geometry/CACornerMask+Convenience.swift @@ -0,0 +1,51 @@ +// +// CACornerMask+Accessors.swift +// BXSwiftUtils +// +// Created by Stefan Fochler on 16.10.18. +// Copyright © 2018 Boinx Software Ltd. & Imagine GbR. All rights reserved. +// + +import QuartzCore.CoreAnimation + +public extension CACornerMask +{ + public static let allCorners: CACornerMask = [ + .layerMaxXMaxYCorner, + .layerMaxXMinYCorner, + .layerMinXMaxYCorner, + .layerMinXMinYCorner + ] + + #if os(macOS) + public static let bottomCorners: CACornerMask = [ + .layerMaxXMinYCorner, + .layerMinXMinYCorner + ] + + public static let topCorners: CACornerMask = [ + .layerMinXMaxYCorner, + .layerMaxXMaxYCorner + ] + #elseif os(iOS) + public static let bottomCorners: CACornerMask = [ + .layerMaxXMaxYCorner, + .layerMinXMaxYCorner + ] + + public static let topCorners: CACornerMask = [ + .layerMinXMinYCorner, + .layerMaxXMinYCorner + ] + #endif + + public static let leftCorners: CACornerMask = [ + .layerMinXMinYCorner, + .layerMinXMaxYCorner + ] + + public static let rightCorners: CACornerMask = [ + .layerMaxXMinYCorner, + .layerMaxXMaxYCorner + ] +} diff --git a/BXSwiftUtils/Math & Geometry/CGPath+RoundedRect.swift b/BXSwiftUtils/Math & Geometry/CGPath+RoundedRect.swift new file mode 100644 index 0000000..72b33e7 --- /dev/null +++ b/BXSwiftUtils/Math & Geometry/CGPath+RoundedRect.swift @@ -0,0 +1,73 @@ +// +// CGPath+RoundedRect.swift +// BXSwiftUtils +// +// Created by Stefan Fochler on 16.10.18. +// Copyright © 2018 Boinx Software Ltd. & Imagine GbR. All rights reserved. +// + +import Foundation +import QuartzCore.CoreAnimation + +public extension CGPath +{ + /** + Creates the path of a rounded rect of size `bounds` where only the cordners of `corners` are rounded using `radius`. + + - parameter bounds: The outer bounds of the rect. + - parameter corners: The corner mask containing the corners to be rounded. If empty, no corners will be rounded. + - parameter radius: The radius to be applied to the rounded corners. + */ + public static func roundedRect(inBounds bounds: CGRect, corners: CACornerMask, radius: CGFloat) -> CGPath + { + let path = CGMutablePath() + + if corners.contains(.layerMinXMaxYCorner) + { + let start = CGPoint(x: bounds.minX, y: bounds.maxY - radius) + path.move(to: start) + path.addArc(tangent1End: start, tangent2End: CGPoint(x: bounds.minX + radius, y: bounds.maxY), radius: radius) + } + else + { + path.move(to: CGPoint(x: bounds.minX, y: bounds.maxY)) + } + + if corners.contains(.layerMaxXMaxYCorner) + { + let start = CGPoint(x: bounds.maxX - radius, y: bounds.maxY) + path.addLine(to: start) + path.addArc(tangent1End: start, tangent2End: CGPoint(x: bounds.maxX, y: bounds.maxY - radius), radius: radius) + } + else + { + path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY)) + } + + if corners.contains(.layerMaxXMinYCorner) + { + let start = CGPoint(x: bounds.maxX, y: bounds.minY + radius) + path.addLine(to: start) + path.addArc(tangent1End: start, tangent2End: CGPoint(x: bounds.maxX - radius, y: bounds.minY), radius: radius) + } + else + { + path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY)) + } + + if corners.contains(.layerMinXMinYCorner) + { + let start = CGPoint(x: bounds.minX + radius, y: bounds.minY) + path.addLine(to: start) + path.addArc(tangent1End: start, tangent2End: CGPoint(x: bounds.minX, y: bounds.minY + radius), radius: radius) + } + else + { + path.addLine(to: CGPoint(x: bounds.minX, y: bounds.minY)) + } + + path.closeSubpath() + + return path + } +}