├── screenshots ├── 1242x2688bb.png ├── 1242x2688bb-2.png ├── 1242x2688bb-3.png └── 1242x2688bb-4.png ├── Managed View ├── img │ └── curtain.png ├── Assets.xcassets │ ├── Contents.json │ ├── Managed View.jpg │ ├── icons8-home.imageset │ │ ├── icons8-home.png │ │ ├── icons8-home-1.png │ │ ├── icons8-home-2.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── Manage View-29.png │ │ ├── Manage View-40.png │ │ ├── Manage View-76.png │ │ ├── Manage View-29@2x.png │ │ ├── Manage View-29@3x.png │ │ ├── Manage View-40@2x.png │ │ ├── Manage View-40@3x.png │ │ ├── Manage View-60@2x.png │ │ ├── Manage View-60@3x.png │ │ ├── Manage View-76@2x.png │ │ ├── Manage View-83.5@2x.png │ │ ├── Manage View marketing icon.png │ │ └── Contents.json │ ├── icons8-back_filled.imageset │ │ ├── icons8-back_filled.png │ │ ├── icons8-back_filled-1.png │ │ ├── icons8-back_filled-2.png │ │ └── Contents.json │ └── icons8-forward_filled.imageset │ │ ├── icons8-forward_filled.png │ │ ├── icons8-forward_filled-1.png │ │ ├── icons8-forward_filled-2.png │ │ └── Contents.json ├── PrivacyInfo.xcprivacy ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── AppDelegate.swift ├── ScannerViewController.swift └── ViewController.swift ├── Pods ├── Target Support Files │ ├── Pods-Managed View │ │ ├── Pods-Managed View.modulemap │ │ ├── Pods-Managed View-dummy.m │ │ ├── Pods-Managed View-umbrella.h │ │ ├── Pods-Managed View.debug.xcconfig │ │ ├── Pods-Managed View.release.xcconfig │ │ ├── Info.plist │ │ ├── Pods-Managed View-acknowledgements.markdown │ │ ├── Pods-Managed View-acknowledgements.plist │ │ ├── Pods-Managed View-resources.sh │ │ └── Pods-Managed View-frameworks.sh │ └── ManagedAppConfigLib │ │ ├── ManagedAppConfigLib.modulemap │ │ ├── ManagedAppConfigLib-dummy.m │ │ ├── ManagedAppConfigLib-prefix.pch │ │ ├── ManagedAppConfigLib-umbrella.h │ │ ├── ManagedAppConfigLib.xcconfig │ │ └── Info.plist ├── Manifest.lock ├── ManagedAppConfigLib │ ├── LICENSE │ ├── README.md │ └── Classes │ │ └── ManagedAppConfig.swift └── Pods.xcodeproj │ └── project.pbxproj ├── Podfile.lock ├── Podfile ├── Managed View.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ ├── IDEWorkspaceChecks.plist │ └── swiftpm │ └── Package.resolved ├── LICENSE ├── .gitignore ├── AppConfig └── specfile.xml ├── README.md └── Managed View.xcodeproj └── project.pbxproj /screenshots/1242x2688bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/screenshots/1242x2688bb.png -------------------------------------------------------------------------------- /Managed View/img/curtain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/img/curtain.png -------------------------------------------------------------------------------- /screenshots/1242x2688bb-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/screenshots/1242x2688bb-2.png -------------------------------------------------------------------------------- /screenshots/1242x2688bb-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/screenshots/1242x2688bb-3.png -------------------------------------------------------------------------------- /screenshots/1242x2688bb-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/screenshots/1242x2688bb-4.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/Managed View.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/Managed View.jpg -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/icons8-home.imageset/icons8-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/icons8-home.imageset/icons8-home.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-29.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-40.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-76.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/icons8-home.imageset/icons8-home-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/icons8-home.imageset/icons8-home-1.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/icons8-home.imageset/icons8-home-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/icons8-home.imageset/icons8-home-2.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-29@2x.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-29@3x.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-40@2x.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-40@3x.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-60@2x.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-60@3x.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-76@2x.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/AppIcon.appiconset/Manage View-83.5@2x.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/AppIcon.appiconset/Manage View marketing icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/AppIcon.appiconset/Manage View marketing icon.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/icons8-back_filled.imageset/icons8-back_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/icons8-back_filled.imageset/icons8-back_filled.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/icons8-back_filled.imageset/icons8-back_filled-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/icons8-back_filled.imageset/icons8-back_filled-1.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/icons8-back_filled.imageset/icons8-back_filled-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/icons8-back_filled.imageset/icons8-back_filled-2.png -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Managed View/Pods-Managed View.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_Managed_View { 2 | umbrella header "Pods-Managed View-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/icons8-forward_filled.imageset/icons8-forward_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/icons8-forward_filled.imageset/icons8-forward_filled.png -------------------------------------------------------------------------------- /Pods/Target Support Files/ManagedAppConfigLib/ManagedAppConfigLib.modulemap: -------------------------------------------------------------------------------- 1 | framework module ManagedAppConfigLib { 2 | umbrella header "ManagedAppConfigLib-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Managed View/Pods-Managed View-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Managed_View : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Managed_View 5 | @end 6 | -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/icons8-forward_filled.imageset/icons8-forward_filled-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/icons8-forward_filled.imageset/icons8-forward_filled-1.png -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/icons8-forward_filled.imageset/icons8-forward_filled-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximlink/managed-view/HEAD/Managed View/Assets.xcassets/icons8-forward_filled.imageset/icons8-forward_filled-2.png -------------------------------------------------------------------------------- /Pods/Target Support Files/ManagedAppConfigLib/ManagedAppConfigLib-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_ManagedAppConfigLib : NSObject 3 | @end 4 | @implementation PodsDummy_ManagedAppConfigLib 5 | @end 6 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - ManagedAppConfigLib (1.0.0) 3 | 4 | DEPENDENCIES: 5 | - ManagedAppConfigLib 6 | 7 | SPEC CHECKSUMS: 8 | ManagedAppConfigLib: ba5324ddebb748d77e60d49a8e1900424b9fca9d 9 | 10 | PODFILE CHECKSUM: 1e2637bc29bc01a98d55fb5c7489dae15d55ce8a 11 | 12 | COCOAPODS: 1.4.0 13 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :ios, '9.3' 3 | 4 | target 'Managed View' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | pod 'ManagedAppConfigLib' 8 | 9 | end 10 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - ManagedAppConfigLib (1.0.0) 3 | 4 | DEPENDENCIES: 5 | - ManagedAppConfigLib 6 | 7 | SPEC CHECKSUMS: 8 | ManagedAppConfigLib: ba5324ddebb748d77e60d49a8e1900424b9fca9d 9 | 10 | PODFILE CHECKSUM: 1e2637bc29bc01a98d55fb5c7489dae15d55ce8a 11 | 12 | COCOAPODS: 1.4.0 13 | -------------------------------------------------------------------------------- /Managed View.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Managed View.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ManagedAppConfigLib/ManagedAppConfigLib-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Managed View/Pods-Managed View-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_Managed_ViewVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_Managed_ViewVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ManagedAppConfigLib/ManagedAppConfigLib-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double ManagedAppConfigLibVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char ManagedAppConfigLibVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Managed View.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "d72d70bf83891ed804aec861e79ede8461b775c3d7d4426309f7d7a1b96626ec", 3 | "pins" : [ 4 | { 5 | "identity" : "managedappconfiglib", 6 | "kind" : "remoteSourceControl", 7 | "location" : "https://github.com/jamf/ManagedAppConfigLib", 8 | "state" : { 9 | "revision" : "d3c00913b018b62a8c7f2805cd6de6f85171aca6", 10 | "version" : "1.1.1" 11 | } 12 | } 13 | ], 14 | "version" : 3 15 | } 16 | -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/icons8-home.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-home-2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-home-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-home.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Managed View/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyAccessedAPITypes 6 | 7 | 8 | NSPrivacyAccessedAPIType 9 | NSPrivacyAccessedAPICategoryUserDefaults 10 | NSPrivacyAccessedAPITypeReasons 11 | 12 | AC6B.1 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/icons8-back_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icons8-back_filled.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "icons8-back_filled-1.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "icons8-back_filled-2.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/icons8-forward_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-forward_filled-2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-forward_filled-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-forward_filled.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Pods/Target Support Files/ManagedAppConfigLib/ManagedAppConfigLib.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/ManagedAppConfigLib 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/ManagedAppConfigLib 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Managed View/Pods-Managed View.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/ManagedAppConfigLib" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/ManagedAppConfigLib/ManagedAppConfigLib.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "ManagedAppConfigLib" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Managed View/Pods-Managed View.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/ManagedAppConfigLib" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/ManagedAppConfigLib/ManagedAppConfigLib.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "ManagedAppConfigLib" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Managed View/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/ManagedAppConfigLib/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/ManagedAppConfigLib/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 JAMF Software LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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 THE AUTHORS OR COPYRIGHT HOLDERS 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. 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 maximlink 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Managed View/Pods-Managed View-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## ManagedAppConfigLib 5 | 6 | Copyright 2017 JAMF Software LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | 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 THE AUTHORS OR COPYRIGHT HOLDERS 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. 13 | 14 | Generated by CocoaPods - https://cocoapods.org 15 | -------------------------------------------------------------------------------- /Pods/ManagedAppConfigLib/README.md: -------------------------------------------------------------------------------- 1 | # ManagedAppConfigLib 2 | 3 | ## Overview 4 | The purpose of ManagedAppConfigLib is to make it that much easier to work with Apple's [Managed App Configuration](https://developer.apple.com/library/content/samplecode/sc2279/Introduction/Intro.html) by providing a few convenience methods. 5 | 6 | ## Installation 7 | ManagedAppConfigLib can be installed via [Cocoapods](https://guides.cocoapods.org/using/getting-started.html) by adding `pod 'ManagedAppConfigLib'` to your Podfile under your desired targets. 8 | 9 | ## Usage 10 | You will need to `import ManagedAppConfigLib` in each file you wish to use it. 11 | 12 | * Retrieve a value set by MDM from the Managed App Configuration: 13 | ```swift 14 | if let deviceId = ManagedAppConfig.shared.getConfigValue(forKey: "deviceId") as? String { 15 | print(deviceId) 16 | } 17 | ``` 18 | 19 | * Register a closure to be executed when the managed app configuration dictionary is changed: 20 | ```swift 21 | let myClosure = { (configDict: [String : Any?]) -> Void in 22 | print("mannaged app configuration changed") 23 | } 24 | ManagedAppConfig.shared.addAppConfigChangedHook(myClosure) 25 | 26 | ``` 27 | 28 | * Place a value into the managed app feedback dictionary: 29 | ```swift 30 | let exampleKey = "errorCount" 31 | let exampleValue = 0 32 | ManagedAppConfig.shared.updateValue(exampleValue, forKey: exampleKey) 33 | 34 | ``` 35 | 36 | -------------------------------------------------------------------------------- /Managed View/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/xcode,swift 2 | 3 | ### Swift ### 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xccheckout 26 | *.xcscmblueprint 27 | 28 | ## Obj-C/Swift specific 29 | *.hmap 30 | *.ipa 31 | *.dSYM.zip 32 | *.dSYM 33 | 34 | ## Playgrounds 35 | timeline.xctimeline 36 | playground.xcworkspace 37 | 38 | # Swift Package Manager 39 | # 40 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 41 | # Packages/ 42 | # Package.pins 43 | .build/ 44 | 45 | # CocoaPods 46 | # 47 | # We recommend against adding the Pods directory to your .gitignore. However 48 | # you should judge for yourself, the pros and cons are mentioned at: 49 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 50 | # 51 | # Pods/ 52 | 53 | # Carthage 54 | # 55 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 56 | # Carthage/Checkouts 57 | 58 | Carthage/Build 59 | 60 | # fastlane 61 | # 62 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 63 | # screenshots whenever they are needed. 64 | # For more information about the recommended setup visit: 65 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 66 | 67 | fastlane/report.xml 68 | fastlane/Preview.html 69 | fastlane/screenshots 70 | fastlane/test_output 71 | 72 | ### Xcode ### 73 | # Xcode 74 | # 75 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 76 | 77 | ## Build generated 78 | 79 | ## Various settings 80 | 81 | ## Other 82 | .DS_STORE 83 | 84 | ### Xcode Patch ### 85 | *.xcodeproj/* 86 | !*.xcodeproj/project.pbxproj 87 | !*.xcodeproj/xcshareddata/ 88 | !*.xcworkspace/contents.xcworkspacedata 89 | /*.gcno 90 | 91 | # End of https://www.gitignore.io/api/xcode,swift 92 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Managed View/Pods-Managed View-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright 2017 JAMF Software LLC 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 22 | 23 | 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 THE AUTHORS OR COPYRIGHT HOLDERS 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. 24 | 25 | License 26 | MIT 27 | Title 28 | ManagedAppConfigLib 29 | Type 30 | PSGroupSpecifier 31 | 32 | 33 | FooterText 34 | Generated by CocoaPods - https://cocoapods.org 35 | Title 36 | 37 | Type 38 | PSGroupSpecifier 39 | 40 | 41 | StringsTable 42 | Acknowledgements 43 | Title 44 | Acknowledgements 45 | 46 | 47 | -------------------------------------------------------------------------------- /Managed View/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleSignature 20 | ???? 21 | CFBundleURLTypes 22 | 23 | 24 | CFBundleTypeRole 25 | Editor 26 | CFBundleURLName 27 | com.aaronmaxim.ManagedView 28 | CFBundleURLSchemes 29 | 30 | managedview 31 | 32 | 33 | 34 | CFBundleVersion 35 | $(CURRENT_PROJECT_VERSION) 36 | LSRequiresIPhoneOS 37 | 38 | NSAppTransportSecurity 39 | 40 | NSAllowsArbitraryLoads 41 | 42 | 43 | NSCameraUsageDescription 44 | Need access to camera to capture QR Code 45 | UILaunchStoryboardName 46 | LaunchScreen 47 | UIMainStoryboardFile 48 | Main 49 | UIRequiredDeviceCapabilities 50 | 51 | armv7 52 | 53 | UIStatusBarHidden 54 | 55 | UISupportedInterfaceOrientations 56 | 57 | UIInterfaceOrientationPortrait 58 | UIInterfaceOrientationLandscapeLeft 59 | UIInterfaceOrientationLandscapeRight 60 | UIInterfaceOrientationPortraitUpsideDown 61 | 62 | UISupportedInterfaceOrientations~ipad 63 | 64 | UIInterfaceOrientationPortrait 65 | UIInterfaceOrientationPortraitUpsideDown 66 | UIInterfaceOrientationLandscapeLeft 67 | UIInterfaceOrientationLandscapeRight 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /Managed View/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Direct View 4 | // 5 | // Created by Aaron Maxim on 8/16/16. 6 | // Copyright © 2016 Aaron Maxim. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | var deepLink: URL! 17 | 18 | func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any)-> Bool { 19 | deepLink = url 20 | return true 21 | } 22 | 23 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 24 | // Override point for customization after application launch. 25 | return true 26 | } 27 | 28 | func applicationWillResignActive(_ application: UIApplication) { 29 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 30 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 31 | } 32 | 33 | func applicationDidEnterBackground(_ application: UIApplication) { 34 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 35 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 36 | } 37 | 38 | func applicationWillEnterForeground(_ application: UIApplication) { 39 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 40 | } 41 | 42 | func applicationDidBecomeActive(_ application: UIApplication) { 43 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 44 | } 45 | 46 | func applicationWillTerminate(_ application: UIApplication) { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Managed View/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "size" : "29x29", 15 | "idiom" : "iphone", 16 | "filename" : "Manage View-29@2x.png", 17 | "scale" : "2x" 18 | }, 19 | { 20 | "size" : "29x29", 21 | "idiom" : "iphone", 22 | "filename" : "Manage View-29@3x.png", 23 | "scale" : "3x" 24 | }, 25 | { 26 | "size" : "40x40", 27 | "idiom" : "iphone", 28 | "filename" : "Manage View-40@2x.png", 29 | "scale" : "2x" 30 | }, 31 | { 32 | "size" : "40x40", 33 | "idiom" : "iphone", 34 | "filename" : "Manage View-40@3x.png", 35 | "scale" : "3x" 36 | }, 37 | { 38 | "size" : "60x60", 39 | "idiom" : "iphone", 40 | "filename" : "Manage View-60@2x.png", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "size" : "60x60", 45 | "idiom" : "iphone", 46 | "filename" : "Manage View-60@3x.png", 47 | "scale" : "3x" 48 | }, 49 | { 50 | "idiom" : "ipad", 51 | "size" : "20x20", 52 | "scale" : "1x" 53 | }, 54 | { 55 | "idiom" : "ipad", 56 | "size" : "20x20", 57 | "scale" : "2x" 58 | }, 59 | { 60 | "size" : "29x29", 61 | "idiom" : "ipad", 62 | "filename" : "Manage View-29.png", 63 | "scale" : "1x" 64 | }, 65 | { 66 | "size" : "29x29", 67 | "idiom" : "ipad", 68 | "filename" : "Manage View-29@2x.png", 69 | "scale" : "2x" 70 | }, 71 | { 72 | "size" : "40x40", 73 | "idiom" : "ipad", 74 | "filename" : "Manage View-40.png", 75 | "scale" : "1x" 76 | }, 77 | { 78 | "size" : "40x40", 79 | "idiom" : "ipad", 80 | "filename" : "Manage View-40@2x.png", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "size" : "76x76", 85 | "idiom" : "ipad", 86 | "filename" : "Manage View-76.png", 87 | "scale" : "1x" 88 | }, 89 | { 90 | "size" : "76x76", 91 | "idiom" : "ipad", 92 | "filename" : "Manage View-76@2x.png", 93 | "scale" : "2x" 94 | }, 95 | { 96 | "size" : "83.5x83.5", 97 | "idiom" : "ipad", 98 | "filename" : "Manage View-83.5@2x.png", 99 | "scale" : "2x" 100 | }, 101 | { 102 | "size" : "1024x1024", 103 | "idiom" : "ios-marketing", 104 | "filename" : "Manage View marketing icon.png", 105 | "scale" : "1x" 106 | } 107 | ], 108 | "info" : { 109 | "version" : 1, 110 | "author" : "xcode" 111 | } 112 | } -------------------------------------------------------------------------------- /Managed View/ScannerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScannerViewController.swift 3 | // QR Code Reader 4 | // 5 | 6 | import AVFoundation 7 | import UIKit 8 | 9 | class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate { 10 | var captureSession: AVCaptureSession! 11 | var previewLayer: AVCaptureVideoPreviewLayer! 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | view.backgroundColor = UIColor.black 17 | captureSession = AVCaptureSession() 18 | 19 | guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return } 20 | let videoInput: AVCaptureDeviceInput 21 | 22 | do { 23 | videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice) 24 | } catch { 25 | return 26 | } 27 | 28 | if (captureSession.canAddInput(videoInput)) { 29 | captureSession.addInput(videoInput) 30 | } else { 31 | failed() 32 | return 33 | } 34 | 35 | let metadataOutput = AVCaptureMetadataOutput() 36 | 37 | if (captureSession.canAddOutput(metadataOutput)) { 38 | captureSession.addOutput(metadataOutput) 39 | 40 | metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) 41 | metadataOutput.metadataObjectTypes = [.qr] 42 | } else { 43 | failed() 44 | return 45 | } 46 | 47 | previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) 48 | previewLayer.frame = view.layer.bounds 49 | previewLayer.videoGravity = .resizeAspectFill 50 | view.layer.addSublayer(previewLayer) 51 | 52 | captureSession.startRunning() 53 | } 54 | 55 | func failed() { 56 | let ac = UIAlertController(title: "Scanning not supported", message: "Your device does not support scanning a code from an item. Please use a device with a camera.", preferredStyle: .alert) 57 | ac.addAction(UIAlertAction(title: "OK", style: .default)) 58 | present(ac, animated: true) 59 | captureSession = nil 60 | } 61 | 62 | override func viewWillAppear(_ animated: Bool) { 63 | super.viewWillAppear(animated) 64 | 65 | if (captureSession?.isRunning == false) { 66 | captureSession.startRunning() 67 | } 68 | } 69 | 70 | override func viewWillDisappear(_ animated: Bool) { 71 | super.viewWillDisappear(animated) 72 | 73 | if (captureSession?.isRunning == true) { 74 | captureSession.stopRunning() 75 | } 76 | } 77 | 78 | func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { 79 | captureSession.stopRunning() 80 | 81 | if let metadataObject = metadataObjects.first { 82 | guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return } 83 | guard let stringValue = readableObject.stringValue else { return } 84 | AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate)) 85 | found(code: stringValue) 86 | } 87 | 88 | dismiss(animated: true) 89 | } 90 | 91 | func found(code: String) { 92 | NSLog("QR Code viewed by camera: \(code)") 93 | 94 | NotificationCenter.default.post(name: ViewController.notificationCamera, object: nil, userInfo: ["qrCode": code]) 95 | 96 | navigationController?.popViewController(animated: true) 97 | } 98 | 99 | } 100 | 101 | -------------------------------------------------------------------------------- /AppConfig/specfile.xml: -------------------------------------------------------------------------------- 1 | 2 | 2.1.2 3 | com.aaronmaxim.ManagedView 4 | 5 | 6 | 7 | https://www.maximlink.com/readme/ 8 | 9 | 10 | 11 | 12 | OFF 13 | 14 | 15 | 16 | 17 | 18 | OFF 19 | 20 | 21 | 22 | 23 | 24 | OFF 25 | 26 | 27 | 28 | 29 | 30 | OFF 31 | 32 | 33 | 34 | 35 | 36 | OFF 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | URL to display in app or default home page when BROWSER_MODE is enabled. 48 | 49 | 50 | 51 | 54 | 55 | Set to “ON” to display static image and provide user a visual that the device is not available. 56 | 57 | 58 | 59 | 62 | 63 | Set to “ON” to display browser navigation bar and provide user an interactive method to navigate web sites. 64 | 65 | 66 | 67 | 70 | 71 | Set to “ON” to disable the ability to edit the URL address bar. Use with content filter via config profile to lock down device to specific website. 72 | 73 | 74 | 75 | 78 | 79 | et to “ON” to remotely trigger Autonomous Single App Mode. Requires supervised device and config profile with ASAM restriction payload. Green bar will be displayed at bottom of app if ASAM is enabled. Gray bar indicates ASAM is not enabled, but REMOTE_LOCK is attempting to enable. 80 | 81 | 82 | 83 | 86 | 87 | Set to “ON” to enable private browsing mode. While in private browsing mode, the app stores web browsing data in non-persistent local data store similar to Safari using Private Browsing mode. 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /Pods/ManagedAppConfigLib/Classes/ManagedAppConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // managed-app-config.swift 3 | // 4 | // Created by James Felton on 2/7/17. 5 | /* Copyright (c) 2017 JAMF Software 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | import Foundation 27 | 28 | public class ManagedAppConfig { 29 | 30 | enum DictionaryType { 31 | case AppConfig 32 | case Feedback 33 | } 34 | 35 | // singleton 36 | public static let shared = ManagedAppConfig() 37 | 38 | private let kFeedbackKey = "com.apple.feedback.managed" 39 | private let kConfigurationKey = "com.apple.configuration.managed" 40 | 41 | private var configHooks = [([String:Any?]) -> Void]() 42 | private var feedbackHooks = [([String:Any?]) -> Void]() 43 | 44 | init() { 45 | // add observer 46 | NotificationCenter.default.addObserver(self, selector: #selector(ManagedAppConfig.didChange), name: UserDefaults.didChangeNotification, object: nil) 47 | } 48 | 49 | // instead of modifying this file's didChange event, allow the registration of closures 50 | public func addAppConfigChangedHook(_ appConfigChangedHook: @escaping ([String:Any?]) -> Void) { 51 | configHooks.append(appConfigChangedHook) 52 | } 53 | 54 | // called when the userdefaults did change notification fires 55 | @objc func didChange() { 56 | if let configDict = UserDefaults.standard.dictionary(forKey: kConfigurationKey) { 57 | for hook in configHooks { 58 | hook(configDict) 59 | } 60 | } 61 | if let feedbackDict = UserDefaults.standard.dictionary(forKey: kFeedbackKey) { 62 | for hook in feedbackHooks { 63 | hook(feedbackDict) 64 | } 65 | } 66 | } 67 | 68 | // MARK - Dictionary getters/setters 69 | public func getConfigValue(forKey: String) -> Any? { 70 | if let myAppConfig = UserDefaults.standard.dictionary(forKey: kConfigurationKey) { 71 | return myAppConfig[forKey] 72 | } 73 | return nil 74 | } 75 | 76 | // public func getFeedbackValue(forKey: String) -> Any? { 77 | // if var myAppConfigFeedback = UserDefaults.standard.dictionary(forKey: kFeedbackKey) { 78 | // return myAppConfigFeedback[forKey] 79 | // } 80 | // return nil 81 | // } 82 | 83 | public func updateValue(_ value: Any, forKey: String) { 84 | if var myAppConfigFeedback = UserDefaults.standard.dictionary(forKey: kFeedbackKey) { 85 | myAppConfigFeedback[forKey] = value 86 | UserDefaults.standard.set(myAppConfigFeedback, forKey: kFeedbackKey) 87 | } else { 88 | // there was no dictionary at all, create one and place the key/value pair in it 89 | let feedbackDict = [forKey: value] 90 | UserDefaults.standard.set(feedbackDict, forKey: kFeedbackKey) 91 | } 92 | } 93 | 94 | 95 | 96 | } 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Managed View 2 | Simple app leveraging Managed App Config and Anonymous Single Add Mode (ASAM). 3 | 4 | Available free in the App Store. 5 | https://apps.apple.com/us/app/managed-view/id1159586083 6 | 7 | ## Requirements 8 | MDM solution such as Jamf Pro or Jamf School to enable managed app configuration 9 | 10 | ## Use cases 11 | - Simple Kiosk – lock device into displaying a specific web page 12 | - Secure Kiosk – add timer to return to homepage and remove cached user data 13 | - Interactive Kiosk– add ability for user to enter URL 14 | - Surveys/Forms – present web page survey from while locking device during survey and automatically unlocking device after survey completed 15 | - Maintenance – present curtained view while device is performing maintenance 16 | 17 | 18 | ## Features 19 | - Managed App Config to define URL from central management console. 20 | - Support for Autonomous Single App Mode (ASAM). ASAM allows user to enable and disable Single App Mode within the app. 21 | - Built-in maintenance mode with embedded image. Enabled with Managed App Config. 22 | - Optional navigation bar for user interaction enabled with Managed App Config. 23 | - Add timer to reset app back to predefined URL. 24 | - Option to delete cached user data when touching homepage button or during timer reset 25 | - Define string in URL and when found will automatically turn off app lock (ASAM). Used with REMOTE_LOCK set to ON 26 | - Added deep link support using "managedview://" URL scheme. Use case includes using multiple web clips to support multiple URLs (version 2.4) 27 | - Added QR code scanning option (version 2.5) 28 | 29 | 30 | ## Managed App Config settings 31 | 32 | URL key: URL to display in app or default home page when BROWSER_MODE is enabled. 33 | 34 | **MAINTENANCE_MODE** key: Set to “ON” to display static image and provide user a visual that the device is not available. 35 | 36 | **BROWSER_MODE** key: Set to “ON” to display browser navigation bar and provide user an interactive method to navigate web sites. 37 | 38 | **BROWSER_BAR_NO_EDIT** key: Set to “ON” to disable the ability to edit the URL address bar. Use with content filter via config profile to lock down device to specific website. 39 | 40 | **REMOTE_LOCK** key: Set to “ON” to remotely trigger Autonomous Single App Mode. Requires supervised device and config profile with ASAM restriction payload. 41 | 42 | **PRIVATE_BROWSING** key: Set to “ON” to enable private browsing mode. While in private browsing mode, the app stores web browsing data in non-persistent local data store similar to Safari using Private Browsing mode. 43 | 44 | **RESET_TIMER** key: Set integer value (in seconds) to set an automatic timer to clear browser data and return to default home page. Timer will not activate if already at homepage. Timer is disable by default or disabled with value of 0. (New in version 2.2) 45 | 46 | **QUERY_URL_STRING**  Advanced option used with REMOTE_LOCK to support automatically unlocking app when a specific URL is presented. Set value to string contained in URL to be unlocked. Supports completed surveys/forms. (new in version 2.3) 47 | 48 | **QR_CODE**  key: Set to “ON” to enable QR Code scanning within the web browser. Setting the value to ON will present camera icon. (new in version 2.5) 49 | 50 | **LAUNCH_DELAY**  key: Set integer value (in seconds) to set delay before web page in reloaded after failed attempt. Handy for when network is not available and workaround for timing issues with newer device hardware. (new in version 2.5) 51 | 52 | ## App Config template 53 | ```xml 54 | 55 | MAINTENANCE_MODE 56 | OFF 57 | BROWSER_MODE 58 | OFF 59 | BROWSER_BAR_NO_EDIT 60 | OFF 61 | URL 62 | https://foo.com 63 | REMOTE_LOCK 64 | OFF 65 | PRIVATE_BROWSING 66 | OFF 67 | RESET_TIMER 68 | 0 69 | QUERY_URL_STRING 70 | 71 | QR_CODE 72 | OFF 73 | LAUNCH_DELAY 74 | 0 75 | 76 | ``` 77 | 78 | Please leave feedback and/or comments on how this could be improved! 79 | 80 | Thanks! Aaron 81 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Managed View/Pods-Managed View-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 14 | 15 | case "${TARGETED_DEVICE_FAMILY}" in 16 | 1,2) 17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 18 | ;; 19 | 1) 20 | TARGET_DEVICE_ARGS="--target-device iphone" 21 | ;; 22 | 2) 23 | TARGET_DEVICE_ARGS="--target-device ipad" 24 | ;; 25 | 3) 26 | TARGET_DEVICE_ARGS="--target-device tv" 27 | ;; 28 | 4) 29 | TARGET_DEVICE_ARGS="--target-device watch" 30 | ;; 31 | *) 32 | TARGET_DEVICE_ARGS="--target-device mac" 33 | ;; 34 | esac 35 | 36 | install_resource() 37 | { 38 | if [[ "$1" = /* ]] ; then 39 | RESOURCE_PATH="$1" 40 | else 41 | RESOURCE_PATH="${PODS_ROOT}/$1" 42 | fi 43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 44 | cat << EOM 45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 46 | EOM 47 | exit 1 48 | fi 49 | case $RESOURCE_PATH in 50 | *.storyboard) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.xib) 55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 57 | ;; 58 | *.framework) 59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 63 | ;; 64 | *.xcdatamodel) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 67 | ;; 68 | *.xcdatamodeld) 69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 71 | ;; 72 | *.xcmappingmodel) 73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 75 | ;; 76 | *.xcassets) 77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 79 | ;; 80 | *) 81 | echo "$RESOURCE_PATH" || true 82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 83 | ;; 84 | esac 85 | } 86 | 87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 92 | fi 93 | rm -f "$RESOURCES_TO_COPY" 94 | 95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 96 | then 97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 99 | while read line; do 100 | if [[ $line != "${PODS_ROOT}*" ]]; then 101 | XCASSET_FILES+=("$line") 102 | fi 103 | done <<<"$OTHER_XCASSETS" 104 | 105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 106 | fi 107 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Managed View/Pods-Managed View-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | # Used as a return value for each invocation of `strip_invalid_archs` function. 10 | STRIP_BINARY_RETVAL=0 11 | 12 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 13 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 14 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 15 | 16 | # Copies and strips a vendored framework 17 | install_framework() 18 | { 19 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 20 | local source="${BUILT_PRODUCTS_DIR}/$1" 21 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 22 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 23 | elif [ -r "$1" ]; then 24 | local source="$1" 25 | fi 26 | 27 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 28 | 29 | if [ -L "${source}" ]; then 30 | echo "Symlinked..." 31 | source="$(readlink "${source}")" 32 | fi 33 | 34 | # Use filter instead of exclude so missing patterns don't throw errors. 35 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 36 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 37 | 38 | local basename 39 | basename="$(basename -s .framework "$1")" 40 | binary="${destination}/${basename}.framework/${basename}" 41 | if ! [ -r "$binary" ]; then 42 | binary="${destination}/${basename}" 43 | fi 44 | 45 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 46 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 47 | strip_invalid_archs "$binary" 48 | fi 49 | 50 | # Resign the code if required by the build settings to avoid unstable apps 51 | code_sign_if_enabled "${destination}/$(basename "$1")" 52 | 53 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 54 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 55 | local swift_runtime_libs 56 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 57 | for lib in $swift_runtime_libs; do 58 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 59 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 60 | code_sign_if_enabled "${destination}/${lib}" 61 | done 62 | fi 63 | } 64 | 65 | # Copies and strips a vendored dSYM 66 | install_dsym() { 67 | local source="$1" 68 | if [ -r "$source" ]; then 69 | # Copy the dSYM into a the targets temp dir. 70 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 71 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 72 | 73 | local basename 74 | basename="$(basename -s .framework.dSYM "$source")" 75 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 76 | 77 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 78 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 79 | strip_invalid_archs "$binary" 80 | fi 81 | 82 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 83 | # Move the stripped file into its final destination. 84 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 85 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 86 | else 87 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 88 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 89 | fi 90 | fi 91 | } 92 | 93 | # Signs a framework with the provided identity 94 | code_sign_if_enabled() { 95 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 96 | # Use the current code_sign_identitiy 97 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 98 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 99 | 100 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 101 | code_sign_cmd="$code_sign_cmd &" 102 | fi 103 | echo "$code_sign_cmd" 104 | eval "$code_sign_cmd" 105 | fi 106 | } 107 | 108 | # Strip invalid architectures 109 | strip_invalid_archs() { 110 | binary="$1" 111 | # Get architectures for current target binary 112 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 113 | # Intersect them with the architectures we are building for 114 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 115 | # If there are no archs supported by this binary then warn the user 116 | if [[ -z "$intersected_archs" ]]; then 117 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 118 | STRIP_BINARY_RETVAL=0 119 | return 120 | fi 121 | stripped="" 122 | for arch in $binary_archs; do 123 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 124 | # Strip non-valid architectures in-place 125 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 126 | stripped="$stripped $arch" 127 | fi 128 | done 129 | if [[ "$stripped" ]]; then 130 | echo "Stripped $binary of architectures:$stripped" 131 | fi 132 | STRIP_BINARY_RETVAL=1 133 | } 134 | 135 | 136 | if [[ "$CONFIGURATION" == "Debug" ]]; then 137 | install_framework "${BUILT_PRODUCTS_DIR}/ManagedAppConfigLib/ManagedAppConfigLib.framework" 138 | fi 139 | if [[ "$CONFIGURATION" == "Release" ]]; then 140 | install_framework "${BUILT_PRODUCTS_DIR}/ManagedAppConfigLib/ManagedAppConfigLib.framework" 141 | fi 142 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 143 | wait 144 | fi 145 | -------------------------------------------------------------------------------- /Managed View/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /Managed View.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 933DFBBA1EB117B1002EEE14 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 933DFBB91EB117B1002EEE14 /* Assets.xcassets */; }; 11 | 933DFBBD1EB117E4002EEE14 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 933DFBBB1EB117E4002EEE14 /* ViewController.swift */; }; 12 | 933DFBBE1EB117E4002EEE14 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 933DFBBC1EB117E4002EEE14 /* AppDelegate.swift */; }; 13 | 933DFBC11EB11818002EEE14 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 933DFBBF1EB11818002EEE14 /* Main.storyboard */; }; 14 | 933DFBC41EB118D4002EEE14 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 933DFBC21EB118D4002EEE14 /* LaunchScreen.storyboard */; }; 15 | 9356E40D1EC26AF400AFECDB /* img in Resources */ = {isa = PBXBuildFile; fileRef = 9356E40C1EC26AF400AFECDB /* img */; }; 16 | 938BD5EF2A76D342001DBC8E /* ScannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 938BD5EE2A76D342001DBC8E /* ScannerViewController.swift */; }; 17 | 93A2DB6B2BBF57E1003F8565 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 93A2DB6A2BBF57E1003F8565 /* PrivacyInfo.xcprivacy */; }; 18 | 93AE68882A4D53DF00A9AE91 /* ManagedAppConfigLib in Frameworks */ = {isa = PBXBuildFile; productRef = 93AE68872A4D53DF00A9AE91 /* ManagedAppConfigLib */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 933DFBB91EB117B1002EEE14 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = "Managed View/Assets.xcassets"; sourceTree = SOURCE_ROOT; }; 23 | 933DFBBB1EB117E4002EEE14 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ViewController.swift; path = "Managed View/ViewController.swift"; sourceTree = SOURCE_ROOT; }; 24 | 933DFBBC1EB117E4002EEE14 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = "Managed View/AppDelegate.swift"; sourceTree = SOURCE_ROOT; }; 25 | 933DFBC01EB11818002EEE14 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = "Managed View/Base.lproj/Main.storyboard"; sourceTree = SOURCE_ROOT; }; 26 | 933DFBC31EB118D4002EEE14 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = "Managed View/Base.lproj/LaunchScreen.storyboard"; sourceTree = SOURCE_ROOT; }; 27 | 933DFBC51EB11ACB002EEE14 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "Managed View/Info.plist"; sourceTree = SOURCE_ROOT; }; 28 | 9356E40C1EC26AF400AFECDB /* img */ = {isa = PBXFileReference; lastKnownFileType = folder; name = img; path = "Managed View/img"; sourceTree = SOURCE_ROOT; }; 29 | 938BD5EE2A76D342001DBC8E /* ScannerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScannerViewController.swift; sourceTree = ""; }; 30 | 938E753D1EB15D89000806F3 /* .gitignore */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; 31 | 938E753F1EB15F74000806F3 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 32 | 93A2DB6A2BBF57E1003F8565 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 33 | 93F367471ECA837200BFC414 /* screenshots */ = {isa = PBXFileReference; lastKnownFileType = folder; path = screenshots; sourceTree = ""; }; 34 | BABE64311D635D7700F4EC2B /* Managed View.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Managed View.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | BABE642E1D635D7700F4EC2B /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | 93AE68882A4D53DF00A9AE91 /* ManagedAppConfigLib in Frameworks */, 43 | ); 44 | runOnlyForDeploymentPostprocessing = 0; 45 | }; 46 | /* End PBXFrameworksBuildPhase section */ 47 | 48 | /* Begin PBXGroup section */ 49 | BABE64281D635D7700F4EC2B = { 50 | isa = PBXGroup; 51 | children = ( 52 | 938E753F1EB15F74000806F3 /* README.md */, 53 | 93F367471ECA837200BFC414 /* screenshots */, 54 | 938E753D1EB15D89000806F3 /* .gitignore */, 55 | BABE64331D635D7700F4EC2B /* Managed View */, 56 | BABE64321D635D7700F4EC2B /* Products */, 57 | ); 58 | sourceTree = ""; 59 | }; 60 | BABE64321D635D7700F4EC2B /* Products */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | BABE64311D635D7700F4EC2B /* Managed View.app */, 64 | ); 65 | name = Products; 66 | sourceTree = ""; 67 | }; 68 | BABE64331D635D7700F4EC2B /* Managed View */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 93A2DB6A2BBF57E1003F8565 /* PrivacyInfo.xcprivacy */, 72 | 938BD5EE2A76D342001DBC8E /* ScannerViewController.swift */, 73 | 9356E40C1EC26AF400AFECDB /* img */, 74 | 933DFBBB1EB117E4002EEE14 /* ViewController.swift */, 75 | 933DFBBC1EB117E4002EEE14 /* AppDelegate.swift */, 76 | 933DFBB91EB117B1002EEE14 /* Assets.xcassets */, 77 | 933DFBBF1EB11818002EEE14 /* Main.storyboard */, 78 | 933DFBC51EB11ACB002EEE14 /* Info.plist */, 79 | 933DFBC21EB118D4002EEE14 /* LaunchScreen.storyboard */, 80 | ); 81 | path = "Managed View"; 82 | sourceTree = ""; 83 | }; 84 | /* End PBXGroup section */ 85 | 86 | /* Begin PBXNativeTarget section */ 87 | BABE64301D635D7700F4EC2B /* Managed View */ = { 88 | isa = PBXNativeTarget; 89 | buildConfigurationList = BABE64431D635D7700F4EC2B /* Build configuration list for PBXNativeTarget "Managed View" */; 90 | buildPhases = ( 91 | BABE642D1D635D7700F4EC2B /* Sources */, 92 | BABE642E1D635D7700F4EC2B /* Frameworks */, 93 | BABE642F1D635D7700F4EC2B /* Resources */, 94 | ); 95 | buildRules = ( 96 | ); 97 | dependencies = ( 98 | ); 99 | name = "Managed View"; 100 | packageProductDependencies = ( 101 | 93AE68872A4D53DF00A9AE91 /* ManagedAppConfigLib */, 102 | ); 103 | productName = "Direct View"; 104 | productReference = BABE64311D635D7700F4EC2B /* Managed View.app */; 105 | productType = "com.apple.product-type.application"; 106 | }; 107 | /* End PBXNativeTarget section */ 108 | 109 | /* Begin PBXProject section */ 110 | BABE64291D635D7700F4EC2B /* Project object */ = { 111 | isa = PBXProject; 112 | attributes = { 113 | BuildIndependentTargetsInParallel = YES; 114 | LastSwiftUpdateCheck = 0730; 115 | LastUpgradeCheck = 1530; 116 | ORGANIZATIONNAME = "Aaron Maxim"; 117 | TargetAttributes = { 118 | BABE64301D635D7700F4EC2B = { 119 | CreatedOnToolsVersion = 7.3.1; 120 | DevelopmentTeam = P994T22Q7J; 121 | LastSwiftMigration = 1100; 122 | }; 123 | }; 124 | }; 125 | buildConfigurationList = BABE642C1D635D7700F4EC2B /* Build configuration list for PBXProject "Managed View" */; 126 | compatibilityVersion = "Xcode 3.2"; 127 | developmentRegion = en; 128 | hasScannedForEncodings = 0; 129 | knownRegions = ( 130 | en, 131 | Base, 132 | ); 133 | mainGroup = BABE64281D635D7700F4EC2B; 134 | packageReferences = ( 135 | 93AE68862A4D53DF00A9AE91 /* XCRemoteSwiftPackageReference "ManagedAppConfigLib" */, 136 | ); 137 | productRefGroup = BABE64321D635D7700F4EC2B /* Products */; 138 | projectDirPath = ""; 139 | projectRoot = ""; 140 | targets = ( 141 | BABE64301D635D7700F4EC2B /* Managed View */, 142 | ); 143 | }; 144 | /* End PBXProject section */ 145 | 146 | /* Begin PBXResourcesBuildPhase section */ 147 | BABE642F1D635D7700F4EC2B /* Resources */ = { 148 | isa = PBXResourcesBuildPhase; 149 | buildActionMask = 2147483647; 150 | files = ( 151 | 9356E40D1EC26AF400AFECDB /* img in Resources */, 152 | 933DFBC41EB118D4002EEE14 /* LaunchScreen.storyboard in Resources */, 153 | 933DFBBA1EB117B1002EEE14 /* Assets.xcassets in Resources */, 154 | 93A2DB6B2BBF57E1003F8565 /* PrivacyInfo.xcprivacy in Resources */, 155 | 933DFBC11EB11818002EEE14 /* Main.storyboard in Resources */, 156 | ); 157 | runOnlyForDeploymentPostprocessing = 0; 158 | }; 159 | /* End PBXResourcesBuildPhase section */ 160 | 161 | /* Begin PBXSourcesBuildPhase section */ 162 | BABE642D1D635D7700F4EC2B /* Sources */ = { 163 | isa = PBXSourcesBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | 933DFBBE1EB117E4002EEE14 /* AppDelegate.swift in Sources */, 167 | 938BD5EF2A76D342001DBC8E /* ScannerViewController.swift in Sources */, 168 | 933DFBBD1EB117E4002EEE14 /* ViewController.swift in Sources */, 169 | ); 170 | runOnlyForDeploymentPostprocessing = 0; 171 | }; 172 | /* End PBXSourcesBuildPhase section */ 173 | 174 | /* Begin PBXVariantGroup section */ 175 | 933DFBBF1EB11818002EEE14 /* Main.storyboard */ = { 176 | isa = PBXVariantGroup; 177 | children = ( 178 | 933DFBC01EB11818002EEE14 /* Base */, 179 | ); 180 | name = Main.storyboard; 181 | sourceTree = ""; 182 | }; 183 | 933DFBC21EB118D4002EEE14 /* LaunchScreen.storyboard */ = { 184 | isa = PBXVariantGroup; 185 | children = ( 186 | 933DFBC31EB118D4002EEE14 /* Base */, 187 | ); 188 | name = LaunchScreen.storyboard; 189 | sourceTree = ""; 190 | }; 191 | /* End PBXVariantGroup section */ 192 | 193 | /* Begin XCBuildConfiguration section */ 194 | BABE64411D635D7700F4EC2B /* Debug */ = { 195 | isa = XCBuildConfiguration; 196 | buildSettings = { 197 | ALWAYS_SEARCH_USER_PATHS = NO; 198 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 199 | CLANG_ANALYZER_NONNULL = YES; 200 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 201 | CLANG_CXX_LIBRARY = "libc++"; 202 | CLANG_ENABLE_MODULES = YES; 203 | CLANG_ENABLE_OBJC_ARC = YES; 204 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 205 | CLANG_WARN_BOOL_CONVERSION = YES; 206 | CLANG_WARN_COMMA = YES; 207 | CLANG_WARN_CONSTANT_CONVERSION = YES; 208 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 209 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 210 | CLANG_WARN_EMPTY_BODY = YES; 211 | CLANG_WARN_ENUM_CONVERSION = YES; 212 | CLANG_WARN_INFINITE_RECURSION = YES; 213 | CLANG_WARN_INT_CONVERSION = YES; 214 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 215 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 216 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 217 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 218 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 219 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 220 | CLANG_WARN_STRICT_PROTOTYPES = YES; 221 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 222 | CLANG_WARN_UNREACHABLE_CODE = YES; 223 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 224 | CODE_SIGN_IDENTITY = "iPhone Developer"; 225 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 226 | COPY_PHASE_STRIP = NO; 227 | DEBUG_ACTIVITY_MODE = ""; 228 | "DEBUG_ACTIVITY_MODE[sdk=iphonesimulator*]" = default; 229 | DEBUG_INFORMATION_FORMAT = dwarf; 230 | ENABLE_STRICT_OBJC_MSGSEND = YES; 231 | ENABLE_TESTABILITY = YES; 232 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 233 | GCC_C_LANGUAGE_STANDARD = gnu99; 234 | GCC_DYNAMIC_NO_PIC = NO; 235 | GCC_NO_COMMON_BLOCKS = YES; 236 | GCC_OPTIMIZATION_LEVEL = 0; 237 | GCC_PREPROCESSOR_DEFINITIONS = ( 238 | "DEBUG=1", 239 | "$(inherited)", 240 | ); 241 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 242 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 243 | GCC_WARN_UNDECLARED_SELECTOR = YES; 244 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 245 | GCC_WARN_UNUSED_FUNCTION = YES; 246 | GCC_WARN_UNUSED_VARIABLE = YES; 247 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 248 | MTL_ENABLE_DEBUG_INFO = YES; 249 | ONLY_ACTIVE_ARCH = YES; 250 | SDKROOT = iphoneos; 251 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 252 | TARGETED_DEVICE_FAMILY = "1,2"; 253 | }; 254 | name = Debug; 255 | }; 256 | BABE64421D635D7700F4EC2B /* Release */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | ALWAYS_SEARCH_USER_PATHS = NO; 260 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 261 | CLANG_ANALYZER_NONNULL = YES; 262 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 263 | CLANG_CXX_LIBRARY = "libc++"; 264 | CLANG_ENABLE_MODULES = YES; 265 | CLANG_ENABLE_OBJC_ARC = YES; 266 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 267 | CLANG_WARN_BOOL_CONVERSION = YES; 268 | CLANG_WARN_COMMA = YES; 269 | CLANG_WARN_CONSTANT_CONVERSION = YES; 270 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 271 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 272 | CLANG_WARN_EMPTY_BODY = YES; 273 | CLANG_WARN_ENUM_CONVERSION = YES; 274 | CLANG_WARN_INFINITE_RECURSION = YES; 275 | CLANG_WARN_INT_CONVERSION = YES; 276 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 277 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 278 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 279 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 280 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 281 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 282 | CLANG_WARN_STRICT_PROTOTYPES = YES; 283 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 284 | CLANG_WARN_UNREACHABLE_CODE = YES; 285 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 286 | CODE_SIGN_IDENTITY = "iPhone Developer"; 287 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 288 | COPY_PHASE_STRIP = NO; 289 | DEBUG_ACTIVITY_MODE = ""; 290 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 291 | ENABLE_NS_ASSERTIONS = NO; 292 | ENABLE_STRICT_OBJC_MSGSEND = YES; 293 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 294 | GCC_C_LANGUAGE_STANDARD = gnu99; 295 | GCC_NO_COMMON_BLOCKS = YES; 296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 298 | GCC_WARN_UNDECLARED_SELECTOR = YES; 299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 300 | GCC_WARN_UNUSED_FUNCTION = YES; 301 | GCC_WARN_UNUSED_VARIABLE = YES; 302 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 303 | MTL_ENABLE_DEBUG_INFO = NO; 304 | SDKROOT = iphoneos; 305 | SWIFT_COMPILATION_MODE = wholemodule; 306 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 307 | TARGETED_DEVICE_FAMILY = "1,2"; 308 | VALIDATE_PRODUCT = YES; 309 | }; 310 | name = Release; 311 | }; 312 | BABE64441D635D7700F4EC2B /* Debug */ = { 313 | isa = XCBuildConfiguration; 314 | buildSettings = { 315 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 316 | CLANG_ENABLE_MODULES = YES; 317 | CURRENT_PROJECT_VERSION = 90; 318 | DEVELOPMENT_TEAM = P994T22Q7J; 319 | INFOPLIST_FILE = "$(SRCROOT)/Managed View/Info.plist"; 320 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 321 | LD_RUNPATH_SEARCH_PATHS = ( 322 | "$(inherited)", 323 | "@executable_path/Frameworks", 324 | ); 325 | MARKETING_VERSION = 2.8.14; 326 | PRODUCT_BUNDLE_IDENTIFIER = com.aaronmaxim.ManagedView; 327 | PRODUCT_NAME = "Managed View"; 328 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 329 | SWIFT_VERSION = 5.0; 330 | TARGETED_DEVICE_FAMILY = "1,2"; 331 | }; 332 | name = Debug; 333 | }; 334 | BABE64451D635D7700F4EC2B /* Release */ = { 335 | isa = XCBuildConfiguration; 336 | buildSettings = { 337 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 338 | CLANG_ENABLE_MODULES = YES; 339 | CURRENT_PROJECT_VERSION = 90; 340 | DEVELOPMENT_TEAM = P994T22Q7J; 341 | INFOPLIST_FILE = "$(SRCROOT)/Managed View/Info.plist"; 342 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 343 | LD_RUNPATH_SEARCH_PATHS = ( 344 | "$(inherited)", 345 | "@executable_path/Frameworks", 346 | ); 347 | MARKETING_VERSION = 2.8.14; 348 | PRODUCT_BUNDLE_IDENTIFIER = com.aaronmaxim.ManagedView; 349 | PRODUCT_NAME = "Managed View"; 350 | SWIFT_VERSION = 5.0; 351 | TARGETED_DEVICE_FAMILY = "1,2"; 352 | }; 353 | name = Release; 354 | }; 355 | /* End XCBuildConfiguration section */ 356 | 357 | /* Begin XCConfigurationList section */ 358 | BABE642C1D635D7700F4EC2B /* Build configuration list for PBXProject "Managed View" */ = { 359 | isa = XCConfigurationList; 360 | buildConfigurations = ( 361 | BABE64411D635D7700F4EC2B /* Debug */, 362 | BABE64421D635D7700F4EC2B /* Release */, 363 | ); 364 | defaultConfigurationIsVisible = 0; 365 | defaultConfigurationName = Release; 366 | }; 367 | BABE64431D635D7700F4EC2B /* Build configuration list for PBXNativeTarget "Managed View" */ = { 368 | isa = XCConfigurationList; 369 | buildConfigurations = ( 370 | BABE64441D635D7700F4EC2B /* Debug */, 371 | BABE64451D635D7700F4EC2B /* Release */, 372 | ); 373 | defaultConfigurationIsVisible = 0; 374 | defaultConfigurationName = Release; 375 | }; 376 | /* End XCConfigurationList section */ 377 | 378 | /* Begin XCRemoteSwiftPackageReference section */ 379 | 93AE68862A4D53DF00A9AE91 /* XCRemoteSwiftPackageReference "ManagedAppConfigLib" */ = { 380 | isa = XCRemoteSwiftPackageReference; 381 | repositoryURL = "https://github.com/jamf/ManagedAppConfigLib"; 382 | requirement = { 383 | kind = exactVersion; 384 | version = 1.1.1; 385 | }; 386 | }; 387 | /* End XCRemoteSwiftPackageReference section */ 388 | 389 | /* Begin XCSwiftPackageProductDependency section */ 390 | 93AE68872A4D53DF00A9AE91 /* ManagedAppConfigLib */ = { 391 | isa = XCSwiftPackageProductDependency; 392 | package = 93AE68862A4D53DF00A9AE91 /* XCRemoteSwiftPackageReference "ManagedAppConfigLib" */; 393 | productName = ManagedAppConfigLib; 394 | }; 395 | /* End XCSwiftPackageProductDependency section */ 396 | }; 397 | rootObject = BABE64291D635D7700F4EC2B /* Project object */; 398 | } 399 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 43945785B1BEF661089999CF42008D10 /* ManagedAppConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC45BF5B85CB5871F4CBDBECAB9BF9D /* ManagedAppConfig.swift */; }; 11 | 6DBAC5B59B78B984B1374B4F57505E95 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6604A7D69453B4569E4E4827FB9155A9 /* Foundation.framework */; }; 12 | 8132168D87386D91E5CCCD29612BA44B /* Pods-Managed View-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = DBB40386BBA0289CF93219C6D0479473 /* Pods-Managed View-dummy.m */; }; 13 | 852FAF110645C327C17542FABF0A1E14 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6604A7D69453B4569E4E4827FB9155A9 /* Foundation.framework */; }; 14 | 8D815CC5BF1017D3B2DB794A779EED24 /* ManagedAppConfigLib-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 55528C8C776487A5C573A444D5C25648 /* ManagedAppConfigLib-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | E3EB239B279A707B87FCD9B7C1389A46 /* ManagedAppConfigLib-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DDBDBA975F0F63AA9D1928D65CB2540 /* ManagedAppConfigLib-dummy.m */; }; 16 | FA53387D6E25DD10DCAC3662805174D4 /* Pods-Managed View-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = E5C6FF8539E68DBF2D2351CCBE4EF042 /* Pods-Managed View-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXContainerItemProxy section */ 20 | 6596B71F50C550BE27EA5BA6C43909DE /* PBXContainerItemProxy */ = { 21 | isa = PBXContainerItemProxy; 22 | containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; 23 | proxyType = 1; 24 | remoteGlobalIDString = 7F279792E494B349A381534B9ABB211A; 25 | remoteInfo = ManagedAppConfigLib; 26 | }; 27 | /* End PBXContainerItemProxy section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 0DDBDBA975F0F63AA9D1928D65CB2540 /* ManagedAppConfigLib-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "ManagedAppConfigLib-dummy.m"; sourceTree = ""; }; 31 | 33103A311A13F4ACB6FB88A45F0013BC /* Pods_Managed_View.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Managed_View.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 440BDECD6E6184FBA05F8D641BCC5F35 /* ManagedAppConfigLib-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ManagedAppConfigLib-prefix.pch"; sourceTree = ""; }; 33 | 55528C8C776487A5C573A444D5C25648 /* ManagedAppConfigLib-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ManagedAppConfigLib-umbrella.h"; sourceTree = ""; }; 34 | 6604A7D69453B4569E4E4827FB9155A9 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 35 | 72903E4F113989E4E26C14BA577E1170 /* Pods-Managed View.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Managed View.release.xcconfig"; sourceTree = ""; }; 36 | 7A7C2E6C7828D91ACDD4B46C3A44ECE0 /* ManagedAppConfigLib.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ManagedAppConfigLib.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 83443630951CC41F9DEF33CA3361E1EA /* Pods-Managed View-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Managed View-acknowledgements.markdown"; sourceTree = ""; }; 38 | 939D6163258C2A0C007C8848 /* Property List.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Property List.plist"; path = "../../../../../Desktop/Property List.plist"; sourceTree = ""; }; 39 | 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 40 | 9E9D4A22EEB510E03EF755F2AEB51195 /* Pods-Managed View.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Managed View.debug.xcconfig"; sourceTree = ""; }; 41 | A86E0B070BCBB8B401987CCF4880CFF7 /* ManagedAppConfigLib.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = ManagedAppConfigLib.modulemap; sourceTree = ""; }; 42 | AAC45BF5B85CB5871F4CBDBECAB9BF9D /* ManagedAppConfig.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ManagedAppConfig.swift; path = Classes/ManagedAppConfig.swift; sourceTree = ""; }; 43 | ADB1330903E8D4E4BD28D69822F682DD /* ManagedAppConfigLib.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = ManagedAppConfigLib.xcconfig; sourceTree = ""; }; 44 | B2902CC82E34EF754DD1E9CAFB0A6104 /* Pods-Managed View-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Managed View-acknowledgements.plist"; sourceTree = ""; }; 45 | B6EDCA05964D5C1FF99CF9D82FD5D693 /* Pods-Managed View-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Managed View-resources.sh"; sourceTree = ""; }; 46 | BB96571B18B6933CBA26E6C6FD22CC4E /* Pods-Managed View-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Managed View-frameworks.sh"; sourceTree = ""; }; 47 | C8F7E3606B1E2049ACC64C430759D56F /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 48 | D2E63E99E6A588FF25A9E383598264E6 /* Pods-Managed View.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Managed View.modulemap"; sourceTree = ""; }; 49 | DBB40386BBA0289CF93219C6D0479473 /* Pods-Managed View-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Managed View-dummy.m"; sourceTree = ""; }; 50 | E5C6FF8539E68DBF2D2351CCBE4EF042 /* Pods-Managed View-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Managed View-umbrella.h"; sourceTree = ""; }; 51 | F223773A71ED0C87EEBB1A30ED8FA0D5 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 17344F0E516234C8C712A49E6080EF53 /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | 852FAF110645C327C17542FABF0A1E14 /* Foundation.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | 720647F8ECCC0C6384E3DDA23B3381CE /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | 6DBAC5B59B78B984B1374B4F57505E95 /* Foundation.framework in Frameworks */, 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXFrameworksBuildPhase section */ 72 | 73 | /* Begin PBXGroup section */ 74 | 5D6163694DA8B5580939C545A6B1719F /* Targets Support Files */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | D64D9D43E2D1E18B5BB8CBE342DA77E1 /* Pods-Managed View */, 78 | ); 79 | name = "Targets Support Files"; 80 | sourceTree = ""; 81 | }; 82 | 7DB346D0F39D3F0E887471402A8071AB = { 83 | isa = PBXGroup; 84 | children = ( 85 | 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, 86 | BC3CA7F9E30CC8F7E2DD044DD34432FC /* Frameworks */, 87 | 956A7B7BA3CBD84E584A46901AB93D01 /* Pods */, 88 | BA5ED4F78D291A26B1965CA0D93C0A51 /* Products */, 89 | 5D6163694DA8B5580939C545A6B1719F /* Targets Support Files */, 90 | ); 91 | sourceTree = ""; 92 | }; 93 | 956A7B7BA3CBD84E584A46901AB93D01 /* Pods */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 9B1093DBE654D4760A70E5D8F9830DDE /* ManagedAppConfigLib */, 97 | ); 98 | name = Pods; 99 | sourceTree = ""; 100 | }; 101 | 9B1093DBE654D4760A70E5D8F9830DDE /* ManagedAppConfigLib */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | AAC45BF5B85CB5871F4CBDBECAB9BF9D /* ManagedAppConfig.swift */, 105 | 939D6163258C2A0C007C8848 /* Property List.plist */, 106 | E9BBBE26F23D2525D86625C1989D2E32 /* Support Files */, 107 | ); 108 | path = ManagedAppConfigLib; 109 | sourceTree = ""; 110 | }; 111 | BA5ED4F78D291A26B1965CA0D93C0A51 /* Products */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 7A7C2E6C7828D91ACDD4B46C3A44ECE0 /* ManagedAppConfigLib.framework */, 115 | 33103A311A13F4ACB6FB88A45F0013BC /* Pods_Managed_View.framework */, 116 | ); 117 | name = Products; 118 | sourceTree = ""; 119 | }; 120 | BC3CA7F9E30CC8F7E2DD044DD34432FC /* Frameworks */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | D35AF013A5F0BAD4F32504907A52519E /* iOS */, 124 | ); 125 | name = Frameworks; 126 | sourceTree = ""; 127 | }; 128 | D35AF013A5F0BAD4F32504907A52519E /* iOS */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 6604A7D69453B4569E4E4827FB9155A9 /* Foundation.framework */, 132 | ); 133 | name = iOS; 134 | sourceTree = ""; 135 | }; 136 | D64D9D43E2D1E18B5BB8CBE342DA77E1 /* Pods-Managed View */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | F223773A71ED0C87EEBB1A30ED8FA0D5 /* Info.plist */, 140 | D2E63E99E6A588FF25A9E383598264E6 /* Pods-Managed View.modulemap */, 141 | 83443630951CC41F9DEF33CA3361E1EA /* Pods-Managed View-acknowledgements.markdown */, 142 | B2902CC82E34EF754DD1E9CAFB0A6104 /* Pods-Managed View-acknowledgements.plist */, 143 | DBB40386BBA0289CF93219C6D0479473 /* Pods-Managed View-dummy.m */, 144 | BB96571B18B6933CBA26E6C6FD22CC4E /* Pods-Managed View-frameworks.sh */, 145 | B6EDCA05964D5C1FF99CF9D82FD5D693 /* Pods-Managed View-resources.sh */, 146 | E5C6FF8539E68DBF2D2351CCBE4EF042 /* Pods-Managed View-umbrella.h */, 147 | 9E9D4A22EEB510E03EF755F2AEB51195 /* Pods-Managed View.debug.xcconfig */, 148 | 72903E4F113989E4E26C14BA577E1170 /* Pods-Managed View.release.xcconfig */, 149 | ); 150 | name = "Pods-Managed View"; 151 | path = "Target Support Files/Pods-Managed View"; 152 | sourceTree = ""; 153 | }; 154 | E9BBBE26F23D2525D86625C1989D2E32 /* Support Files */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | C8F7E3606B1E2049ACC64C430759D56F /* Info.plist */, 158 | A86E0B070BCBB8B401987CCF4880CFF7 /* ManagedAppConfigLib.modulemap */, 159 | ADB1330903E8D4E4BD28D69822F682DD /* ManagedAppConfigLib.xcconfig */, 160 | 0DDBDBA975F0F63AA9D1928D65CB2540 /* ManagedAppConfigLib-dummy.m */, 161 | 440BDECD6E6184FBA05F8D641BCC5F35 /* ManagedAppConfigLib-prefix.pch */, 162 | 55528C8C776487A5C573A444D5C25648 /* ManagedAppConfigLib-umbrella.h */, 163 | ); 164 | name = "Support Files"; 165 | path = "../Target Support Files/ManagedAppConfigLib"; 166 | sourceTree = ""; 167 | }; 168 | /* End PBXGroup section */ 169 | 170 | /* Begin PBXHeadersBuildPhase section */ 171 | 49E3D13A6D7484D6DB899A391BA87FD9 /* Headers */ = { 172 | isa = PBXHeadersBuildPhase; 173 | buildActionMask = 2147483647; 174 | files = ( 175 | 8D815CC5BF1017D3B2DB794A779EED24 /* ManagedAppConfigLib-umbrella.h in Headers */, 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | 62DF025E6E8DDF3A63B039BBFA6F2A48 /* Headers */ = { 180 | isa = PBXHeadersBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | FA53387D6E25DD10DCAC3662805174D4 /* Pods-Managed View-umbrella.h in Headers */, 184 | ); 185 | runOnlyForDeploymentPostprocessing = 0; 186 | }; 187 | /* End PBXHeadersBuildPhase section */ 188 | 189 | /* Begin PBXNativeTarget section */ 190 | 7F279792E494B349A381534B9ABB211A /* ManagedAppConfigLib */ = { 191 | isa = PBXNativeTarget; 192 | buildConfigurationList = AA84E2749CF44DB4F5EA9BB9B7F55A80 /* Build configuration list for PBXNativeTarget "ManagedAppConfigLib" */; 193 | buildPhases = ( 194 | CD42A483F75F6AB7F9BC3CF37A99BDD8 /* Sources */, 195 | 17344F0E516234C8C712A49E6080EF53 /* Frameworks */, 196 | 49E3D13A6D7484D6DB899A391BA87FD9 /* Headers */, 197 | ); 198 | buildRules = ( 199 | ); 200 | dependencies = ( 201 | ); 202 | name = ManagedAppConfigLib; 203 | productName = ManagedAppConfigLib; 204 | productReference = 7A7C2E6C7828D91ACDD4B46C3A44ECE0 /* ManagedAppConfigLib.framework */; 205 | productType = "com.apple.product-type.framework"; 206 | }; 207 | BB629FA91ADB5C05C5A08CDA21AD1D6A /* Pods-Managed View */ = { 208 | isa = PBXNativeTarget; 209 | buildConfigurationList = E991182EAAEC2893F91F6B49A462E951 /* Build configuration list for PBXNativeTarget "Pods-Managed View" */; 210 | buildPhases = ( 211 | 6431EC7FAF55F71FB2EA37B5FDDA1D81 /* Sources */, 212 | 720647F8ECCC0C6384E3DDA23B3381CE /* Frameworks */, 213 | 62DF025E6E8DDF3A63B039BBFA6F2A48 /* Headers */, 214 | ); 215 | buildRules = ( 216 | ); 217 | dependencies = ( 218 | 258391FA0AB66DC4FB91A0DEDA02E0EE /* PBXTargetDependency */, 219 | ); 220 | name = "Pods-Managed View"; 221 | productName = "Pods-Managed View"; 222 | productReference = 33103A311A13F4ACB6FB88A45F0013BC /* Pods_Managed_View.framework */; 223 | productType = "com.apple.product-type.framework"; 224 | }; 225 | /* End PBXNativeTarget section */ 226 | 227 | /* Begin PBXProject section */ 228 | D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { 229 | isa = PBXProject; 230 | attributes = { 231 | LastSwiftUpdateCheck = 0930; 232 | LastUpgradeCheck = 1220; 233 | TargetAttributes = { 234 | 7F279792E494B349A381534B9ABB211A = { 235 | LastSwiftMigration = 1100; 236 | }; 237 | }; 238 | }; 239 | buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; 240 | compatibilityVersion = "Xcode 3.2"; 241 | developmentRegion = en; 242 | hasScannedForEncodings = 0; 243 | knownRegions = ( 244 | en, 245 | Base, 246 | ); 247 | mainGroup = 7DB346D0F39D3F0E887471402A8071AB; 248 | productRefGroup = BA5ED4F78D291A26B1965CA0D93C0A51 /* Products */; 249 | projectDirPath = ""; 250 | projectRoot = ""; 251 | targets = ( 252 | 7F279792E494B349A381534B9ABB211A /* ManagedAppConfigLib */, 253 | BB629FA91ADB5C05C5A08CDA21AD1D6A /* Pods-Managed View */, 254 | ); 255 | }; 256 | /* End PBXProject section */ 257 | 258 | /* Begin PBXSourcesBuildPhase section */ 259 | 6431EC7FAF55F71FB2EA37B5FDDA1D81 /* Sources */ = { 260 | isa = PBXSourcesBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | 8132168D87386D91E5CCCD29612BA44B /* Pods-Managed View-dummy.m in Sources */, 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | }; 267 | CD42A483F75F6AB7F9BC3CF37A99BDD8 /* Sources */ = { 268 | isa = PBXSourcesBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | 43945785B1BEF661089999CF42008D10 /* ManagedAppConfig.swift in Sources */, 272 | E3EB239B279A707B87FCD9B7C1389A46 /* ManagedAppConfigLib-dummy.m in Sources */, 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | }; 276 | /* End PBXSourcesBuildPhase section */ 277 | 278 | /* Begin PBXTargetDependency section */ 279 | 258391FA0AB66DC4FB91A0DEDA02E0EE /* PBXTargetDependency */ = { 280 | isa = PBXTargetDependency; 281 | name = ManagedAppConfigLib; 282 | target = 7F279792E494B349A381534B9ABB211A /* ManagedAppConfigLib */; 283 | targetProxy = 6596B71F50C550BE27EA5BA6C43909DE /* PBXContainerItemProxy */; 284 | }; 285 | /* End PBXTargetDependency section */ 286 | 287 | /* Begin XCBuildConfiguration section */ 288 | 1AF222D5907D42CF42D37A35E9106BB6 /* Release */ = { 289 | isa = XCBuildConfiguration; 290 | baseConfigurationReference = ADB1330903E8D4E4BD28D69822F682DD /* ManagedAppConfigLib.xcconfig */; 291 | buildSettings = { 292 | CODE_SIGN_IDENTITY = ""; 293 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 294 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 295 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 296 | CURRENT_PROJECT_VERSION = 1; 297 | DEFINES_MODULE = YES; 298 | DYLIB_COMPATIBILITY_VERSION = 1; 299 | DYLIB_CURRENT_VERSION = 1; 300 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 301 | GCC_PREFIX_HEADER = "Target Support Files/ManagedAppConfigLib/ManagedAppConfigLib-prefix.pch"; 302 | INFOPLIST_FILE = "Target Support Files/ManagedAppConfigLib/Info.plist"; 303 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 304 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 305 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 306 | MODULEMAP_FILE = "Target Support Files/ManagedAppConfigLib/ManagedAppConfigLib.modulemap"; 307 | PRODUCT_NAME = ManagedAppConfigLib; 308 | SDKROOT = iphoneos; 309 | SKIP_INSTALL = YES; 310 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; 311 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 312 | SWIFT_VERSION = 5.0; 313 | TARGETED_DEVICE_FAMILY = "1,2"; 314 | VALIDATE_PRODUCT = YES; 315 | VERSIONING_SYSTEM = "apple-generic"; 316 | VERSION_INFO_PREFIX = ""; 317 | }; 318 | name = Release; 319 | }; 320 | 5011B865762FC0C0A28ECEC4CB63DC68 /* Debug */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ALWAYS_SEARCH_USER_PATHS = NO; 324 | CLANG_ANALYZER_NONNULL = YES; 325 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 326 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 327 | CLANG_CXX_LIBRARY = "libc++"; 328 | CLANG_ENABLE_MODULES = YES; 329 | CLANG_ENABLE_OBJC_ARC = YES; 330 | CLANG_ENABLE_OBJC_WEAK = YES; 331 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 332 | CLANG_WARN_BOOL_CONVERSION = YES; 333 | CLANG_WARN_COMMA = YES; 334 | CLANG_WARN_CONSTANT_CONVERSION = YES; 335 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 336 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 337 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 338 | CLANG_WARN_EMPTY_BODY = YES; 339 | CLANG_WARN_ENUM_CONVERSION = YES; 340 | CLANG_WARN_INFINITE_RECURSION = YES; 341 | CLANG_WARN_INT_CONVERSION = YES; 342 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 343 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 344 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 345 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 346 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 347 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 348 | CLANG_WARN_STRICT_PROTOTYPES = YES; 349 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 350 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 351 | CLANG_WARN_UNREACHABLE_CODE = YES; 352 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 353 | CODE_SIGNING_REQUIRED = NO; 354 | COPY_PHASE_STRIP = NO; 355 | DEBUG_INFORMATION_FORMAT = dwarf; 356 | ENABLE_STRICT_OBJC_MSGSEND = YES; 357 | ENABLE_TESTABILITY = YES; 358 | GCC_C_LANGUAGE_STANDARD = gnu11; 359 | GCC_DYNAMIC_NO_PIC = NO; 360 | GCC_NO_COMMON_BLOCKS = YES; 361 | GCC_OPTIMIZATION_LEVEL = 0; 362 | GCC_PREPROCESSOR_DEFINITIONS = ( 363 | "POD_CONFIGURATION_DEBUG=1", 364 | "DEBUG=1", 365 | "$(inherited)", 366 | ); 367 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 368 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 369 | GCC_WARN_UNDECLARED_SELECTOR = YES; 370 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 371 | GCC_WARN_UNUSED_FUNCTION = YES; 372 | GCC_WARN_UNUSED_VARIABLE = YES; 373 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 374 | MTL_ENABLE_DEBUG_INFO = YES; 375 | ONLY_ACTIVE_ARCH = YES; 376 | PRODUCT_NAME = "$(TARGET_NAME)"; 377 | PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; 378 | STRIP_INSTALLED_PRODUCT = NO; 379 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 380 | SYMROOT = "${SRCROOT}/../build"; 381 | }; 382 | name = Debug; 383 | }; 384 | 7ADC00943BF9BD99E95DE3856AFD0326 /* Debug */ = { 385 | isa = XCBuildConfiguration; 386 | baseConfigurationReference = ADB1330903E8D4E4BD28D69822F682DD /* ManagedAppConfigLib.xcconfig */; 387 | buildSettings = { 388 | CODE_SIGN_IDENTITY = ""; 389 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 390 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 391 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 392 | CURRENT_PROJECT_VERSION = 1; 393 | DEFINES_MODULE = YES; 394 | DYLIB_COMPATIBILITY_VERSION = 1; 395 | DYLIB_CURRENT_VERSION = 1; 396 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 397 | GCC_PREFIX_HEADER = "Target Support Files/ManagedAppConfigLib/ManagedAppConfigLib-prefix.pch"; 398 | INFOPLIST_FILE = "Target Support Files/ManagedAppConfigLib/Info.plist"; 399 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 400 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 401 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 402 | MODULEMAP_FILE = "Target Support Files/ManagedAppConfigLib/ManagedAppConfigLib.modulemap"; 403 | PRODUCT_NAME = ManagedAppConfigLib; 404 | SDKROOT = iphoneos; 405 | SKIP_INSTALL = YES; 406 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; 407 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 408 | SWIFT_VERSION = 5.0; 409 | TARGETED_DEVICE_FAMILY = "1,2"; 410 | VERSIONING_SYSTEM = "apple-generic"; 411 | VERSION_INFO_PREFIX = ""; 412 | }; 413 | name = Debug; 414 | }; 415 | 827269710C98D60C14D46BC6AF9BF728 /* Release */ = { 416 | isa = XCBuildConfiguration; 417 | buildSettings = { 418 | ALWAYS_SEARCH_USER_PATHS = NO; 419 | CLANG_ANALYZER_NONNULL = YES; 420 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 421 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 422 | CLANG_CXX_LIBRARY = "libc++"; 423 | CLANG_ENABLE_MODULES = YES; 424 | CLANG_ENABLE_OBJC_ARC = YES; 425 | CLANG_ENABLE_OBJC_WEAK = YES; 426 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 427 | CLANG_WARN_BOOL_CONVERSION = YES; 428 | CLANG_WARN_COMMA = YES; 429 | CLANG_WARN_CONSTANT_CONVERSION = YES; 430 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 431 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 432 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 433 | CLANG_WARN_EMPTY_BODY = YES; 434 | CLANG_WARN_ENUM_CONVERSION = YES; 435 | CLANG_WARN_INFINITE_RECURSION = YES; 436 | CLANG_WARN_INT_CONVERSION = YES; 437 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 438 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 439 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 440 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 441 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 442 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 443 | CLANG_WARN_STRICT_PROTOTYPES = YES; 444 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 445 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 446 | CLANG_WARN_UNREACHABLE_CODE = YES; 447 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 448 | CODE_SIGNING_REQUIRED = NO; 449 | COPY_PHASE_STRIP = NO; 450 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 451 | ENABLE_NS_ASSERTIONS = NO; 452 | ENABLE_STRICT_OBJC_MSGSEND = YES; 453 | GCC_C_LANGUAGE_STANDARD = gnu11; 454 | GCC_NO_COMMON_BLOCKS = YES; 455 | GCC_PREPROCESSOR_DEFINITIONS = ( 456 | "POD_CONFIGURATION_RELEASE=1", 457 | "$(inherited)", 458 | ); 459 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 460 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 461 | GCC_WARN_UNDECLARED_SELECTOR = YES; 462 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 463 | GCC_WARN_UNUSED_FUNCTION = YES; 464 | GCC_WARN_UNUSED_VARIABLE = YES; 465 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 466 | MTL_ENABLE_DEBUG_INFO = NO; 467 | PRODUCT_NAME = "$(TARGET_NAME)"; 468 | PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; 469 | STRIP_INSTALLED_PRODUCT = NO; 470 | SYMROOT = "${SRCROOT}/../build"; 471 | }; 472 | name = Release; 473 | }; 474 | BC6BABB46501E3E5C662540D82274B0F /* Release */ = { 475 | isa = XCBuildConfiguration; 476 | baseConfigurationReference = 72903E4F113989E4E26C14BA577E1170 /* Pods-Managed View.release.xcconfig */; 477 | buildSettings = { 478 | CODE_SIGN_IDENTITY = ""; 479 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 480 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 481 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 482 | CURRENT_PROJECT_VERSION = 1; 483 | DEFINES_MODULE = YES; 484 | DYLIB_COMPATIBILITY_VERSION = 1; 485 | DYLIB_CURRENT_VERSION = 1; 486 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 487 | INFOPLIST_FILE = "Target Support Files/Pods-Managed View/Info.plist"; 488 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 489 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 490 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 491 | MACH_O_TYPE = staticlib; 492 | MODULEMAP_FILE = "Target Support Files/Pods-Managed View/Pods-Managed View.modulemap"; 493 | OTHER_LDFLAGS = ""; 494 | OTHER_LIBTOOLFLAGS = ""; 495 | PODS_ROOT = "$(SRCROOT)"; 496 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 497 | PRODUCT_NAME = Pods_Managed_View; 498 | SDKROOT = iphoneos; 499 | SKIP_INSTALL = YES; 500 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 501 | TARGETED_DEVICE_FAMILY = "1,2"; 502 | VALIDATE_PRODUCT = YES; 503 | VERSIONING_SYSTEM = "apple-generic"; 504 | VERSION_INFO_PREFIX = ""; 505 | }; 506 | name = Release; 507 | }; 508 | D9970178E1F936DCBD68F6E3C72FAE65 /* Debug */ = { 509 | isa = XCBuildConfiguration; 510 | baseConfigurationReference = 9E9D4A22EEB510E03EF755F2AEB51195 /* Pods-Managed View.debug.xcconfig */; 511 | buildSettings = { 512 | CODE_SIGN_IDENTITY = ""; 513 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 514 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 515 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 516 | CURRENT_PROJECT_VERSION = 1; 517 | DEFINES_MODULE = YES; 518 | DYLIB_COMPATIBILITY_VERSION = 1; 519 | DYLIB_CURRENT_VERSION = 1; 520 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 521 | INFOPLIST_FILE = "Target Support Files/Pods-Managed View/Info.plist"; 522 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 523 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 524 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 525 | MACH_O_TYPE = staticlib; 526 | MODULEMAP_FILE = "Target Support Files/Pods-Managed View/Pods-Managed View.modulemap"; 527 | OTHER_LDFLAGS = ""; 528 | OTHER_LIBTOOLFLAGS = ""; 529 | PODS_ROOT = "$(SRCROOT)"; 530 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 531 | PRODUCT_NAME = Pods_Managed_View; 532 | SDKROOT = iphoneos; 533 | SKIP_INSTALL = YES; 534 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 535 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 536 | TARGETED_DEVICE_FAMILY = "1,2"; 537 | VERSIONING_SYSTEM = "apple-generic"; 538 | VERSION_INFO_PREFIX = ""; 539 | }; 540 | name = Debug; 541 | }; 542 | /* End XCBuildConfiguration section */ 543 | 544 | /* Begin XCConfigurationList section */ 545 | 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { 546 | isa = XCConfigurationList; 547 | buildConfigurations = ( 548 | 5011B865762FC0C0A28ECEC4CB63DC68 /* Debug */, 549 | 827269710C98D60C14D46BC6AF9BF728 /* Release */, 550 | ); 551 | defaultConfigurationIsVisible = 0; 552 | defaultConfigurationName = Release; 553 | }; 554 | AA84E2749CF44DB4F5EA9BB9B7F55A80 /* Build configuration list for PBXNativeTarget "ManagedAppConfigLib" */ = { 555 | isa = XCConfigurationList; 556 | buildConfigurations = ( 557 | 7ADC00943BF9BD99E95DE3856AFD0326 /* Debug */, 558 | 1AF222D5907D42CF42D37A35E9106BB6 /* Release */, 559 | ); 560 | defaultConfigurationIsVisible = 0; 561 | defaultConfigurationName = Release; 562 | }; 563 | E991182EAAEC2893F91F6B49A462E951 /* Build configuration list for PBXNativeTarget "Pods-Managed View" */ = { 564 | isa = XCConfigurationList; 565 | buildConfigurations = ( 566 | D9970178E1F936DCBD68F6E3C72FAE65 /* Debug */, 567 | BC6BABB46501E3E5C662540D82274B0F /* Release */, 568 | ); 569 | defaultConfigurationIsVisible = 0; 570 | defaultConfigurationName = Release; 571 | }; 572 | /* End XCConfigurationList section */ 573 | }; 574 | rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; 575 | } 576 | -------------------------------------------------------------------------------- /Managed View/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Managed View 4 | // 5 | 6 | import Foundation 7 | import UIKit 8 | @preconcurrency import WebKit 9 | import ManagedAppConfigLib 10 | 11 | class ViewController: UIViewController, UITextFieldDelegate, WKUIDelegate, WKNavigationDelegate, UIScrollViewDelegate, UIGestureRecognizerDelegate { 12 | 13 | @IBOutlet weak var browserURL: UITextField! //BROWSER MODE ONLY: URL address bar 14 | var webView: WKWebView? 15 | 16 | // Keep track of all webViews created by createWebViewWith 17 | private var additionalWebViews: [WKWebView] = [] 18 | 19 | var defaultURL = URL(string: "https://maximlink.com/readme") 20 | 21 | var blockLockFlag = false 22 | 23 | // Add flag to prevent multiple webView creation attempts 24 | private var isCreatingWebView = false 25 | private var needsWebViewUpdate = false 26 | 27 | // Add constraint reference for dynamic updates 28 | private var webViewBottomConstraint: NSLayoutConstraint? 29 | 30 | // Add flag to prevent multiple toolbar setups 31 | private var isSettingUpToolbar = false 32 | 33 | // Reference to the storyboard toolbar 34 | @IBOutlet weak var storyboardToolbar: UIToolbar? 35 | 36 | // Loading indicator components 37 | private var loadingIndicator: UIActivityIndicatorView? 38 | private var loadingBackgroundView: UIView? 39 | private var loadingLabel: UILabel? 40 | 41 | // Add flag to prevent double taps on browser buttons 42 | private var isBrowserBarAnimating = false 43 | 44 | // Navigation bar button outlets for enabling/disabling during loading 45 | @IBOutlet weak var backButton: UIBarButtonItem? 46 | @IBOutlet weak var forwardButton: UIBarButtonItem? 47 | @IBOutlet weak var refreshButton: UIBarButtonItem? 48 | @IBOutlet weak var homeButton: UIBarButtonItem? 49 | 50 | // local app configuration 51 | struct Config { 52 | var decodeURL: String // decode URL 53 | var maintenanceMode: String // display curtain image 54 | var newURL: URL! // new URL request 55 | var previousURL: URL! // previously loaded URL 56 | var browserMode: String // display user interactive browser controls 57 | var browserModeNoEdit: String // disable address bar edit 58 | var homeURL: URL! // BROWSER MODE ONLY: URL for home button 59 | var remoteLock: String // enable remote ASAM capability 60 | var currentASAMStatus: String // current ASAM status 61 | var privateBrowsing: String // private browsing mode 62 | var queryUrlString: String // private browsing mode 63 | var resetTimer: Int // timer in seconds to reset session 64 | var qrCode: String // enable QR Code reader 65 | var launchDelay: Int // initial page load delayed by seconds 66 | var detectScroll: String // reset timer if scrolling 67 | var redirect: String // redirect new tabs / pop-ups to webview 68 | var disabletrust: String // accept unsecure SSL 69 | var autoOpenPopup: String // allow javascipt to auto open popup 70 | var disableAppConfigListener: String // disable managed app config listener 71 | var brightness: Int // device brightness control (-1=disabled, 0-100=brightness %) 72 | var resetTimerOnHome: String // enable reset timer when at home URL 73 | 74 | 75 | var displayURL: URL { 76 | if maintenanceMode == "ON" { // display curtain image 77 | return URL.init(fileURLWithPath: Bundle.main.path(forResource: "curtain", ofType: "png", inDirectory: "img")!) 78 | } 79 | else { // or new URL request 80 | return newURL 81 | } 82 | } 83 | } 84 | 85 | // set local configuration defaults 86 | var config = Config(decodeURL: "OFF", 87 | maintenanceMode: "OFF", 88 | newURL: URL(string: ""), 89 | previousURL: URL(string: ""), 90 | browserMode: "OFF", 91 | browserModeNoEdit: "OFF", 92 | homeURL: URL(string: ""), 93 | remoteLock: "OFF", 94 | currentASAMStatus: "OFF", 95 | privateBrowsing: "OFF", 96 | queryUrlString: "", 97 | resetTimer: 0, 98 | qrCode: "OFF", 99 | launchDelay: 0, 100 | detectScroll: "OFF", 101 | redirect: "OFF", 102 | disabletrust: "OFF", 103 | autoOpenPopup: "OFF", 104 | disableAppConfigListener: "OFF", 105 | brightness: -1, 106 | resetTimerOnHome: "OFF" 107 | ) 108 | 109 | var timer: Timer? 110 | 111 | // version 2.5 - observe when camera reads QR Code 112 | static let notificationCamera = Notification.Name("qrCode") 113 | 114 | // version 2.5 - if error (e.g. network not connected) then retry page load after x.x seconds 115 | let retryTimer = 1.0 116 | 117 | // WKWebView setup via code - required for < iOS 11 118 | override func loadView() { 119 | super.loadView() 120 | // Initial webView creation will happen in viewDidLoad 121 | } 122 | 123 | // Hide Top Status Bar 124 | override var prefersStatusBarHidden: Bool { 125 | return true 126 | } 127 | 128 | @objc func appCameToForeGround(notification: Notification) { 129 | print("App in foreground") 130 | deepLink() 131 | } 132 | 133 | override func viewDidLoad() { 134 | super.viewDidLoad() 135 | 136 | // Configure navigation bar appearance to respect system appearance mode 137 | configureNavigationBarAppearance() 138 | 139 | if let delay = ManagedAppConfig.shared.getConfigValue(forKey: "LAUNCH_DELAY") { 140 | DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(delay as! Int)) { // seconds delay 141 | self.readManagedAppConfig() } 142 | } else { 143 | DispatchQueue.main.async {self.readManagedAppConfig()} 144 | } 145 | 146 | let myClosure = { (configDict: [String : Any?]) -> Void in 147 | print("Managed app configuration changed") 148 | // version - 2.8.6 149 | if self.config.disableAppConfigListener == "OFF" { 150 | self.readManagedAppConfig() // reload MDM managed app config to local config 151 | } 152 | } 153 | // listen for managed app config updates 154 | if config.queryUrlString == "" { 155 | ManagedAppConfig.shared.addAppConfigChangedHook(myClosure) 156 | } 157 | 158 | // version 2.8.10 - add device lock detection 159 | print("Device lock detection enabled") 160 | addDeviceLockDetection() 161 | 162 | NotificationCenter.default.addObserver(self, 163 | selector: #selector(appCameToForeGround(notification:)), 164 | name: UIApplication.willEnterForegroundNotification, 165 | object: nil) 166 | 167 | deepLink() // initial check if app launched by deep link 168 | 169 | NotificationCenter.default.addObserver(self, selector: #selector(onNotification(notification:)), name: ViewController.notificationCamera, object: nil) 170 | } 171 | 172 | override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { 173 | super.traitCollectionDidChange(previousTraitCollection) 174 | 175 | // Reconfigure appearance when appearance mode changes 176 | if #available(iOS 13.0, *) { 177 | if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) { 178 | configureNavigationBarAppearance() 179 | } 180 | } 181 | } 182 | 183 | private func configureNavigationBarAppearance() { 184 | guard let navigationController = navigationController else { return } 185 | 186 | if #available(iOS 13.0, *) { 187 | // Use the new appearance API for iOS 13+ 188 | let appearance = UINavigationBarAppearance() 189 | appearance.configureWithDefaultBackground() // This will respect system appearance 190 | 191 | navigationController.navigationBar.standardAppearance = appearance 192 | navigationController.navigationBar.scrollEdgeAppearance = appearance 193 | navigationController.navigationBar.compactAppearance = appearance 194 | 195 | // Remove any fixed tint colors to allow system colors 196 | navigationController.navigationBar.barTintColor = nil 197 | navigationController.navigationBar.backgroundColor = nil 198 | } else { 199 | // For iOS 12 and earlier, use system default 200 | navigationController.navigationBar.barTintColor = nil 201 | navigationController.navigationBar.backgroundColor = nil 202 | navigationController.navigationBar.barStyle = .default 203 | } 204 | } 205 | 206 | func readManagedAppConfig() { 207 | // default managed app config settings 208 | let macDict = [ 209 | "DECODE_URL":"OFF", 210 | "MAINTENANCE_MODE":"OFF", 211 | "URL":String(describing: defaultURL!), 212 | "REMOTE_LOCK":"OFF", 213 | "BROWSER_MODE":"OFF", 214 | "BROWSER_BAR_NO_EDIT":"OFF", 215 | "PRIVATE_BROWSING":"OFF", 216 | "QUERY_URL_STRING":"", 217 | "RESET_TIMER":0, 218 | "QR_CODE":"OFF", 219 | "LAUNCH_DELAY":0, 220 | "DETECT_SCROLL":"OFF", 221 | "REDIRECT_SUPPORT":"OFF", 222 | "DISABLE_TRUST":"OFF", 223 | "AUTO_OPEN_POPUP":"OFF", 224 | "DISABLE_APP_CONFIG_LISTENER":"OFF", 225 | "BRIGHTNESS":-1, 226 | "RESET_TIMER_ON_HOME":"OFF" 227 | ] as [String : Any] 228 | 229 | // Store previous private browsing setting to check if it changed 230 | let previousPrivateBrowsing = config.privateBrowsing 231 | 232 | // determine if MDM pushed managed app config and assign to local config, if not use defaults 233 | for (key,defaultValue) in macDict { 234 | if let value = ManagedAppConfig.shared.getConfigValue(forKey: key) { 235 | switch key { 236 | case "MAINTENANCE_MODE" : config.maintenanceMode = value as! String 237 | case "URL" : do { 238 | if config.decodeURL != "ON" { 239 | self.config.newURL = URL(string: value as! String) 240 | self.config.homeURL = URL(string: value as! String) 241 | print("DEBUG: homeURL set to: \(String(describing: self.config.homeURL))") 242 | } else { 243 | //version 2.8 244 | let string = value as! String 245 | let decoded = string.stringByDecodingHTMLEntities 246 | print("decoded: \(decoded)") 247 | self.config.newURL = URL(string: decoded) 248 | self.config.homeURL = URL(string: decoded) 249 | print("DEBUG: homeURL set to (decoded): \(String(describing: self.config.homeURL))") 250 | } 251 | } 252 | case "REMOTE_LOCK" : config.remoteLock = value as! String 253 | case "BROWSER_MODE" : config.browserMode = value as! String 254 | case "BROWSER_BAR_NO_EDIT" : config.browserModeNoEdit = value as! String 255 | case "PRIVATE_BROWSING" : config.privateBrowsing = value as! String 256 | case "QUERY_URL_STRING" : config.queryUrlString = value as! String 257 | case "RESET_TIMER" : config.resetTimer = value as! Int 258 | case "QR_CODE" : config.qrCode = value as! String 259 | case "LAUNCH_DELAY" : config.launchDelay = value as! Int 260 | case "DETECT_SCROLL" : config.detectScroll = value as! String 261 | case "REDIRECT_SUPPORT" : config.redirect = value as! String 262 | case "DISABLE_TRUST" : config.disabletrust = value as! String 263 | case "AUTO_OPEN_POPUP" : config.autoOpenPopup = value as! String 264 | case "DECODE_URL" : config.decodeURL = value as! String 265 | case "DISABLE_APP_CONFIG_LISTENER" : config.disableAppConfigListener = value as! String 266 | case "BRIGHTNESS" : config.brightness = value as! Int 267 | case "RESET_TIMER_ON_HOME" : config.resetTimerOnHome = value as! String 268 | 269 | default: print("ERROR: \(key) - undefined managed app config key") } 270 | } else { 271 | switch key { 272 | case "MAINTENANCE_MODE" : config.maintenanceMode = defaultValue as! String 273 | case "URL" : do { 274 | self.config.newURL = URL(string: defaultValue as! String) 275 | self.config.homeURL = URL(string: defaultValue as! String) 276 | print("DEBUG: homeURL set to default: \(String(describing: self.config.homeURL))") 277 | } 278 | case "REMOTE_LOCK" : config.remoteLock = defaultValue as! String 279 | case "BROWSER_MODE" : config.browserMode = defaultValue as! String 280 | case "BROWSER_BAR_NO_EDIT" : config.browserModeNoEdit = defaultValue as! String 281 | case "PRIVATE_BROWSING" : config.privateBrowsing = defaultValue as! String 282 | case "QUERY_URL_STRING" : config.queryUrlString = defaultValue as! String 283 | case "RESET_TIMER" : config.resetTimer = defaultValue as! Int 284 | case "QR_CODE" : config.qrCode = defaultValue as! String 285 | case "LAUNCH_DELAY" : config.launchDelay = defaultValue as! Int 286 | case "DETECT_SCROLL" : config.detectScroll = defaultValue as! String 287 | case "REDIRECT_SUPPORT" : config.redirect = defaultValue as! String 288 | case "DISABLE_TRUST" : config.disabletrust = defaultValue as! String 289 | case "AUTO_OPEN_POPUP" : config.autoOpenPopup = defaultValue as! String 290 | case "DECODE_URL" : config.decodeURL = defaultValue as! String 291 | case "DISABLE_APP_CONFIG_LISTENER" : config.disableAppConfigListener = defaultValue as! String 292 | case "BRIGHTNESS" : config.brightness = defaultValue as! Int 293 | case "RESET_TIMER_ON_HOME" : config.resetTimerOnHome = defaultValue as! String 294 | 295 | default: print("ERROR: \(key) - undefined managed app config key") } 296 | } 297 | } 298 | 299 | // switch ASAM setting if new request is different than previous 300 | if self.config.currentASAMStatus != config.remoteLock { 301 | switchRemoteLock() 302 | } 303 | 304 | // Only create new webView if private browsing setting changed or webView doesn't exist 305 | let needsPrivateBrowsingChange = (webView == nil || previousPrivateBrowsing != config.privateBrowsing) 306 | 307 | if needsPrivateBrowsingChange { 308 | updateWebViewIfNeeded() 309 | } else if webView != nil { 310 | // If webView already exists and settings haven't changed, just load the new URL 311 | DispatchQueue.main.async { 312 | self.loadWebViewIfNeeded() 313 | } 314 | } else { 315 | // Create webView if it doesn't exist 316 | updateWebViewIfNeeded() 317 | } 318 | 319 | // check for browser mode status and set accordingly 320 | DispatchQueue.main.async { 321 | self.checkBrowserMode() 322 | self.setBrightness() 323 | } 324 | 325 | print(String(describing: config)) 326 | } 327 | 328 | // MARK: - Brightness Control 329 | private func setBrightness() { 330 | // Only set brightness if the value is >= 0 (-1 means disabled/default) 331 | guard config.brightness >= 0 else { 332 | print("Brightness setting is -1 (disabled), skipping brightness control") 333 | return 334 | } 335 | 336 | // Ensure the value is within valid range (0-100) 337 | let clampedValue = max(0, min(100, config.brightness)) 338 | 339 | // Convert from 0-100 range to 0.0-1.0 range for UIScreen.brightness 340 | let screenBrightness = Float(clampedValue) / 100.0 341 | 342 | DispatchQueue.main.async { 343 | UIScreen.main.brightness = CGFloat(screenBrightness) 344 | print("Device brightness set to: \(clampedValue)% (\(screenBrightness))") 345 | } 346 | } 347 | 348 | // MARK: - Loading Indicator 349 | private func createLoadingIndicator() { 350 | // Only create if it doesn't already exist 351 | guard loadingIndicator == nil else { return } 352 | 353 | // Create background view 354 | loadingBackgroundView = UIView() 355 | loadingBackgroundView?.backgroundColor = UIColor.black.withAlphaComponent(0.5) 356 | loadingBackgroundView?.translatesAutoresizingMaskIntoConstraints = false 357 | 358 | // Create activity indicator 359 | if #available(iOS 13.0, *) { 360 | loadingIndicator = UIActivityIndicatorView(style: .large) 361 | } else { 362 | loadingIndicator = UIActivityIndicatorView(style: .whiteLarge) 363 | } 364 | loadingIndicator?.color = .white 365 | loadingIndicator?.translatesAutoresizingMaskIntoConstraints = false 366 | 367 | // Create loading label 368 | loadingLabel = UILabel() 369 | loadingLabel?.text = "Loading..." 370 | loadingLabel?.textColor = .white 371 | loadingLabel?.font = UIFont.systemFont(ofSize: 16) 372 | loadingLabel?.translatesAutoresizingMaskIntoConstraints = false 373 | 374 | guard let backgroundView = loadingBackgroundView, 375 | let indicator = loadingIndicator, 376 | let label = loadingLabel else { return } 377 | 378 | // Add to view hierarchy 379 | view.addSubview(backgroundView) 380 | backgroundView.addSubview(indicator) 381 | backgroundView.addSubview(label) 382 | 383 | // Set up constraints to cover entire screen including navigation bar 384 | NSLayoutConstraint.activate([ 385 | // Background view fills entire screen, extending beyond safe area 386 | backgroundView.topAnchor.constraint(equalTo: view.topAnchor), 387 | backgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor), 388 | backgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor), 389 | backgroundView.bottomAnchor.constraint(equalTo: view.bottomAnchor), 390 | 391 | // Center activity indicator 392 | indicator.centerXAnchor.constraint(equalTo: backgroundView.centerXAnchor), 393 | indicator.centerYAnchor.constraint(equalTo: backgroundView.centerYAnchor), 394 | 395 | // Position label below indicator 396 | label.centerXAnchor.constraint(equalTo: backgroundView.centerXAnchor), 397 | label.topAnchor.constraint(equalTo: indicator.bottomAnchor, constant: 16) 398 | ]) 399 | 400 | // Initially hidden 401 | backgroundView.isHidden = true 402 | } 403 | 404 | // MARK: - Navigation Button Control 405 | private func setNavigationButtonsEnabled(_ enabled: Bool) { 406 | DispatchQueue.main.async { 407 | // Only control buttons if they exist and browser mode is ON 408 | guard self.config.browserMode == "ON" else { return } 409 | 410 | // Get navigation bar buttons programmatically since outlets may not be connected 411 | if let leftBarButtonItems = self.navigationItem.leftBarButtonItems { 412 | for button in leftBarButtonItems { 413 | button.isEnabled = enabled 414 | } 415 | } 416 | 417 | if let rightBarButtonItems = self.navigationItem.rightBarButtonItems { 418 | for button in rightBarButtonItems { 419 | button.isEnabled = enabled 420 | } 421 | } 422 | 423 | // Also try the outlet approach if they're connected 424 | self.backButton?.isEnabled = enabled 425 | self.forwardButton?.isEnabled = enabled 426 | self.refreshButton?.isEnabled = enabled 427 | self.homeButton?.isEnabled = enabled 428 | 429 | print("Navigation buttons \(enabled ? "enabled" : "disabled")") 430 | } 431 | } 432 | 433 | private func showLoadingIndicator() { 434 | DispatchQueue.main.async { 435 | // Create if needed 436 | self.createLoadingIndicator() 437 | 438 | // Disable navigation buttons during loading 439 | self.setNavigationButtonsEnabled(false) 440 | 441 | // Show and start animating 442 | self.loadingBackgroundView?.isHidden = false 443 | self.loadingIndicator?.startAnimating() 444 | 445 | // Add to navigation controller's view if available to cover nav bar, otherwise use our view 446 | let targetView = self.navigationController?.view ?? self.view! 447 | 448 | // Remove from current parent if it exists 449 | self.loadingBackgroundView?.removeFromSuperview() 450 | 451 | // Add to the target view 452 | if let backgroundView = self.loadingBackgroundView { 453 | targetView.addSubview(backgroundView) 454 | 455 | // Update constraints for the new parent view 456 | backgroundView.translatesAutoresizingMaskIntoConstraints = false 457 | NSLayoutConstraint.activate([ 458 | backgroundView.topAnchor.constraint(equalTo: targetView.topAnchor), 459 | backgroundView.leadingAnchor.constraint(equalTo: targetView.leadingAnchor), 460 | backgroundView.trailingAnchor.constraint(equalTo: targetView.trailingAnchor), 461 | backgroundView.bottomAnchor.constraint(equalTo: targetView.bottomAnchor) 462 | ]) 463 | 464 | // Bring to front to ensure it's visible above everything 465 | targetView.bringSubviewToFront(backgroundView) 466 | } 467 | } 468 | } 469 | 470 | private func hideLoadingIndicator() { 471 | DispatchQueue.main.async { 472 | self.loadingBackgroundView?.isHidden = true 473 | self.loadingIndicator?.stopAnimating() 474 | 475 | // Re-enable navigation buttons when loading is complete 476 | self.setNavigationButtonsEnabled(true) 477 | } 478 | } 479 | 480 | private func updateWebViewIfNeeded() { 481 | // Prevent multiple simultaneous webView creation attempts 482 | guard !isCreatingWebView else { 483 | needsWebViewUpdate = true 484 | return 485 | } 486 | 487 | isCreatingWebView = true 488 | needsWebViewUpdate = false 489 | 490 | let isPrivate = (config.privateBrowsing == "ON") 491 | 492 | // Clean up existing webView if it exists 493 | if let existingWebView = webView { 494 | DispatchQueue.main.async { 495 | existingWebView.removeFromSuperview() 496 | self.webView = nil 497 | self.createWebView(isPrivate: isPrivate) 498 | } 499 | } else { 500 | // Create new webView on main queue 501 | DispatchQueue.main.async { 502 | self.createWebView(isPrivate: isPrivate) 503 | } 504 | } 505 | } 506 | 507 | private func createWebView(isPrivate: Bool) { 508 | // Clean up any existing webView 509 | webView?.removeFromSuperview() 510 | webViewBottomConstraint = nil 511 | 512 | let webConfiguration = WKWebViewConfiguration() 513 | if isPrivate { 514 | webConfiguration.websiteDataStore = WKWebsiteDataStore.nonPersistent() 515 | } 516 | 517 | // version 2.8.2 - auto open popup 518 | if config.autoOpenPopup == "ON" { 519 | print("Auto open popup ON") 520 | webConfiguration.preferences.javaScriptCanOpenWindowsAutomatically = true 521 | } 522 | 523 | webView = WKWebView(frame: .zero, configuration: webConfiguration) 524 | guard let webView = webView else { 525 | isCreatingWebView = false 526 | return 527 | } 528 | 529 | webView.uiDelegate = self 530 | webView.navigationDelegate = self 531 | browserURL.delegate = self 532 | if #available(iOS 11.0, *) { 533 | webView.scrollView.contentInsetAdjustmentBehavior = .never 534 | } 535 | 536 | view.addSubview(webView) 537 | webView.translatesAutoresizingMaskIntoConstraints = false 538 | 539 | // Set up constraints - use different bottom constraint based on QR code setting 540 | let bottomAnchor: NSLayoutYAxisAnchor 541 | if config.qrCode == "ON" { 542 | // Respect safe area to avoid toolbar overlap 543 | bottomAnchor = view.safeAreaLayoutGuide.bottomAnchor 544 | } else { 545 | // Extend to bottom of screen 546 | bottomAnchor = view.bottomAnchor 547 | } 548 | 549 | // Create and store the bottom constraint for later updates 550 | webViewBottomConstraint = webView.bottomAnchor.constraint(equalTo: bottomAnchor) 551 | 552 | NSLayoutConstraint.activate([ 553 | webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), 554 | webView.leadingAnchor.constraint(equalTo: view.leadingAnchor), 555 | webView.trailingAnchor.constraint(equalTo: view.trailingAnchor), 556 | webViewBottomConstraint! 557 | ]) 558 | 559 | webView.scrollView.delegate = self 560 | 561 | addUserActivityDetection() 562 | 563 | if isPrivate { 564 | print("Created webview - non-persistent") 565 | } else { 566 | print("Created webview - persistent") 567 | } 568 | 569 | // Reset the creation flag 570 | isCreatingWebView = false 571 | 572 | // Check if another update was requested while we were creating 573 | if needsWebViewUpdate { 574 | DispatchQueue.main.async { 575 | self.updateWebViewIfNeeded() 576 | } 577 | } else { 578 | // Load initial URL 579 | loadWebViewIfNeeded() 580 | } 581 | } 582 | 583 | // load new URL request & check scheme (v2.3.1) 584 | // version 2.8.12 - updated check scheme method 585 | func loadWebViewIfNeeded() { 586 | guard let webView = webView else { return } 587 | 588 | var finalURL = config.displayURL 589 | 590 | // Check if scheme is nil or managedview and set to https 591 | if config.displayURL.scheme == nil || config.displayURL.scheme == "managedview"{ 592 | // Get the URL string and prepend https:// 593 | let urlString = config.displayURL.absoluteString 594 | if let httpsURL = URL(string: "https://" + urlString) { 595 | finalURL = httpsURL 596 | } 597 | } 598 | 599 | // Show loading indicator when starting to load 600 | showLoadingIndicator() 601 | 602 | let myRequest = URLRequest(url: finalURL) 603 | webView.load(myRequest) 604 | 605 | config.previousURL = finalURL 606 | } 607 | 608 | func switchRemoteLock() { 609 | if (config.remoteLock == "ON") { 610 | UIAccessibility.requestGuidedAccessSession(enabled: true, completionHandler: { 611 | success in 612 | 613 | if success { 614 | print("Remote ASAM=ON success") 615 | self.config.currentASAMStatus = "ON" 616 | if let navController = self.navigationController { 617 | navController.toolbar.barTintColor = UIColor.init(red: 0.2, green: 0.6, blue: 0.0, alpha: 1.0) 618 | } 619 | } else { 620 | print("Remote ASAM=ON failure") 621 | } 622 | }) 623 | } 624 | 625 | else { 626 | self.navigationController?.isToolbarHidden = true 627 | 628 | UIAccessibility.requestGuidedAccessSession(enabled: false, completionHandler: { 629 | success in 630 | 631 | if success { 632 | self.config.currentASAMStatus = "OFF" 633 | print("Remote ASAM=OFF success") 634 | } else { 635 | print("Remote ASAM=OFF failure") 636 | } 637 | }) 638 | } 639 | } 640 | 641 | func checkBrowserMode() { 642 | if config.browserMode == "ON" { 643 | navigationController?.isNavigationBarHidden = false 644 | navigationController?.hidesBarsOnSwipe = true 645 | if config.browserModeNoEdit == "ON" { 646 | browserURL.isEnabled = false 647 | } 648 | 649 | } else { 650 | navigationController?.isNavigationBarHidden = true 651 | navigationController?.hidesBarsOnSwipe = false 652 | } 653 | 654 | // Always hide the navigation controller's toolbar since we use the storyboard one 655 | navigationController?.isToolbarHidden = true 656 | self.toolbarItems = nil 657 | 658 | // Find the storyboard toolbar 659 | var storyboardToolbar: UIToolbar? 660 | for subview in view.subviews { 661 | if let toolbar = subview as? UIToolbar { 662 | storyboardToolbar = toolbar 663 | break 664 | } 665 | } 666 | 667 | if config.qrCode == "ON" { 668 | storyboardToolbar?.isHidden = false 669 | updateWebViewBottomConstraint(usesSafeArea: true) 670 | } else { 671 | storyboardToolbar?.isHidden = true 672 | updateWebViewBottomConstraint(usesSafeArea: false) 673 | } 674 | 675 | view.layoutIfNeeded() 676 | } 677 | 678 | private func updateWebViewBottomConstraint(usesSafeArea: Bool) { 679 | guard let webView = webView, let bottomConstraint = webViewBottomConstraint else { return } 680 | 681 | // Find the storyboard toolbar to get its position 682 | var storyboardToolbar: UIToolbar? 683 | for subview in view.subviews { 684 | if let toolbar = subview as? UIToolbar { 685 | storyboardToolbar = toolbar 686 | break 687 | } 688 | } 689 | 690 | // Deactivate current constraint 691 | bottomConstraint.isActive = false 692 | 693 | // Create new constraint with appropriate anchor 694 | let newBottomAnchor: NSLayoutYAxisAnchor 695 | if usesSafeArea && storyboardToolbar?.isHidden == false { 696 | // If toolbar is visible, position web view above it 697 | newBottomAnchor = storyboardToolbar?.topAnchor ?? view.safeAreaLayoutGuide.bottomAnchor 698 | } else { 699 | // Extend to bottom of screen when toolbar is hidden 700 | newBottomAnchor = view.bottomAnchor 701 | } 702 | 703 | webViewBottomConstraint = webView.bottomAnchor.constraint(equalTo: newBottomAnchor) 704 | webViewBottomConstraint?.isActive = true 705 | } 706 | 707 | @objc func presentCamera() { 708 | print("camera button pushed") 709 | performSegue(withIdentifier: "cameraSeque", sender: nil) 710 | } 711 | // BROWSER MODE ONLY: 4 connectors to UI 712 | @IBAction func goBack(_ sender: Any) { 713 | provideBrowserButtonFeedback(for: sender) 714 | webView?.goBack() 715 | } 716 | @IBAction func goForward(_ sender: Any) { 717 | provideBrowserButtonFeedback(for: sender) 718 | webView?.goForward() 719 | } 720 | @IBAction func refreshPage(_ sender: Any) { 721 | provideBrowserButtonFeedback(for: sender) 722 | webView?.reload() 723 | } 724 | @IBAction func goHome(_ sender: Any) { 725 | provideBrowserButtonFeedback(for: sender) 726 | self.resetSession() 727 | } 728 | 729 | // MARK: - Browser Button Feedback 730 | private func provideBrowserButtonFeedback(for sender: Any) { 731 | // Prevent double taps during animation 732 | guard !isBrowserBarAnimating else { 733 | print("Browser bar animation in progress, ignoring tap") 734 | return 735 | } 736 | 737 | // Only proceed if browser mode is ON and navigation bar is visible 738 | guard config.browserMode == "ON", 739 | let navigationController = navigationController, 740 | !navigationController.isNavigationBarHidden else { 741 | print("Browser mode off or navigation bar already hidden") 742 | return 743 | } 744 | 745 | isBrowserBarAnimating = true 746 | 747 | // Haptic feedback 748 | let impactFeedback = UIImpactFeedbackGenerator(style: .medium) 749 | impactFeedback.impactOccurred() 750 | 751 | // Hide navigation bar with animation (0.4 seconds) 752 | UIView.animate(withDuration: 0.4, animations: { 753 | navigationController.setNavigationBarHidden(true, animated: false) 754 | self.view.layoutIfNeeded() 755 | }) { _ in 756 | // Show navigation bar again after a brief delay (0.2s delay + 0.4s animation = 1.0s total) 757 | UIView.animate(withDuration: 0.4, delay: 0.2, options: [], animations: { 758 | navigationController.setNavigationBarHidden(false, animated: false) 759 | self.view.layoutIfNeeded() 760 | }) { _ in 761 | self.isBrowserBarAnimating = false 762 | } 763 | } 764 | } 765 | 766 | // BROWSER MODE ONLY: enable user input via URL address bar 767 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 768 | textField.resignFirstResponder() // hide the keyboard 769 | 770 | guard let webView = webView else { return true } 771 | 772 | let userURL = URL(string: browserURL.text ?? "") 773 | config.newURL = userURL 774 | 775 | loadWebViewIfNeeded() 776 | 777 | return true 778 | } 779 | 780 | @objc func fireTimer() { 781 | print("Timer fired!") 782 | resetSession() 783 | } 784 | 785 | func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { 786 | // Hide loading indicator when page finishes loading 787 | hideLoadingIndicator() 788 | 789 | UIApplication.shared.isNetworkActivityIndicatorVisible = true 790 | 791 | if let url = webView.url { 792 | self.browserURL.text = String(describing: url) 793 | } 794 | 795 | // version 2.2 - timer to refresh session 796 | if config.resetTimer != 0 { 797 | timer?.invalidate() 798 | print("Timer reset!") 799 | print("DEBUG: Current webView URL: \(String(describing: webView.url))") 800 | print("DEBUG: Config homeURL: \(String(describing: config.homeURL))") 801 | print("DEBUG: resetTimerOnHome setting: \(config.resetTimerOnHome)") 802 | 803 | // Check if we should start timer based on current URL and resetTimerOnHome setting 804 | let shouldStartTimer: Bool 805 | if config.resetTimerOnHome == "ON" { 806 | // When resetTimerOnHome is ON, always start the timer regardless of URL 807 | shouldStartTimer = true 808 | print("DEBUG: Timer will start (resetTimerOnHome is ON)") 809 | } else { 810 | // Default behavior: only start timer when NOT at home URL 811 | shouldStartTimer = (webView.url != config.homeURL) 812 | print("DEBUG: webView.url = \(String(describing: webView.url))") 813 | print("DEBUG: Timer will \(shouldStartTimer ? "start" : "NOT start") (default behavior, at home: \(!shouldStartTimer))") 814 | } 815 | 816 | if shouldStartTimer { 817 | print("Timer started!") 818 | timer = Timer.scheduledTimer(timeInterval: TimeInterval(config.resetTimer), target: self, selector: #selector(fireTimer), userInfo: nil, repeats: false) 819 | } 820 | } 821 | 822 | // version 2.3 - check for URL string 823 | if config.queryUrlString != "" { 824 | let queryString = config.queryUrlString 825 | let hasSubstring = browserURL.text?.contains(queryString) ?? false 826 | 827 | if hasSubstring { 828 | print("Found string in URL") 829 | self.blockLockFlag = true 830 | 831 | self.navigationController?.isToolbarHidden = true 832 | 833 | DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { // seconds delay 834 | 835 | UIAccessibility.requestGuidedAccessSession(enabled: false, completionHandler: { 836 | success in 837 | 838 | if success { 839 | self.config.currentASAMStatus = "OFF" 840 | print("Remote ASAM=OFF success") 841 | } else { 842 | print("Remote ASAM=OFF failure") 843 | } 844 | }) 845 | } 846 | } 847 | else { 848 | self.blockLockFlag = false 849 | switchRemoteLock() 850 | } 851 | } 852 | } 853 | 854 | // version 2.8.5 855 | func resetSession() { 856 | timer?.invalidate() 857 | 858 | guard let webView = webView else { return } 859 | 860 | print("Resetting session...") 861 | // Remove all additional webViews first 862 | for additionalWebView in additionalWebViews { 863 | additionalWebView.stopLoading() 864 | additionalWebView.removeFromSuperview() 865 | } 866 | additionalWebViews.removeAll() 867 | 868 | // Clear sessionStorage, localStorage, and cookies using JavaScript 869 | let javascript = """ 870 | if (typeof sessionStorage !== 'undefined') { 871 | sessionStorage.clear(); 872 | } 873 | if (typeof localStorage !== 'undefined') { 874 | localStorage.clear(); 875 | } 876 | if (typeof document.cookie !== 'undefined') { 877 | document.cookie.split(";").forEach(function(c) { 878 | document.cookie = c.replace(/^ +/, "").replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/"); 879 | }); 880 | } 881 | """ 882 | 883 | webView.evaluateJavaScript(javascript) { (_, error) in 884 | if let error = error { 885 | print("JavaScript execution error: \(error)") 886 | } 887 | 888 | // Clear WKWebView website data store (this is crucial for Microsoft login) 889 | let websiteDataTypes = WKWebsiteDataStore.allWebsiteDataTypes() 890 | let dataStore = webView.configuration.websiteDataStore 891 | 892 | dataStore.removeData(ofTypes: websiteDataTypes, modifiedSince: Date(timeIntervalSince1970: 0)) { 893 | print("Website data cleared") 894 | 895 | // Load home URL after clearing data 896 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { 897 | self.config.newURL = self.config.homeURL 898 | self.loadWebViewIfNeeded() 899 | } 900 | } 901 | } 902 | } 903 | 904 | // version 2.4 - deep link support 905 | func deepLink() { 906 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: { 907 | let appDelegate = UIApplication.shared.delegate as! AppDelegate 908 | if appDelegate.deepLink != nil { 909 | print("deepLink: \(appDelegate.deepLink!)") 910 | self.config.newURL = appDelegate.deepLink 911 | self.config.homeURL = appDelegate.deepLink 912 | DispatchQueue.main.async { 913 | self.loadWebViewIfNeeded() 914 | } 915 | } 916 | else { 917 | print("No deep link") } 918 | }) 919 | } 920 | 921 | // version 2.5 - observe when camera reads QR Code 922 | @objc func onNotification(notification:Notification) { 923 | print("observed") 924 | if let urlString = notification.userInfo?["qrCode"] as? String { 925 | let url = URL(string: urlString) 926 | print("QR Code loading...") 927 | if let url = url { 928 | print(url.absoluteString) 929 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: { 930 | // Show loading indicator when loading QR code URL 931 | self.showLoadingIndicator() 932 | self.webView?.load(URLRequest(url: url)) 933 | }) 934 | } 935 | } 936 | } 937 | 938 | // version 2.5 - if error (e.g. network not connected) then retry page load after x.x seconds 939 | func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { 940 | // Hide loading indicator on error 941 | hideLoadingIndicator() 942 | 943 | // Log comprehensive error details 944 | print("ERROR: didFailProvisionalNavigation - \(error.localizedDescription)") 945 | print("ERROR: Error domain: \(error._domain)") 946 | print("ERROR: Error code: \(error._code)") 947 | print("ERROR: Failed URL: \(webView.url?.absoluteString ?? "unknown")") 948 | print("ERROR: Target URL: \(config.displayURL.absoluteString)") 949 | 950 | // Log additional error details if it's an NSError 951 | if let nsError = error as NSError? { 952 | print("ERROR: NSError userInfo: \(nsError.userInfo)") 953 | if let failingURL = nsError.userInfo[NSURLErrorFailingURLErrorKey] as? URL { 954 | print("ERROR: Failing URL from userInfo: \(failingURL.absoluteString)") 955 | } 956 | if let failingURLString = nsError.userInfo[NSURLErrorFailingURLStringErrorKey] as? String { 957 | print("ERROR: Failing URL string from userInfo: \(failingURLString)") 958 | } 959 | } 960 | 961 | // Check if this is a cancellation error (NSURLErrorCancelled = -999) 962 | // These should NOT trigger retries as they indicate intentional cancellation 963 | if let nsError = error as NSError?, 964 | nsError.domain == NSURLErrorDomain && nsError.code == NSURLErrorCancelled { 965 | print("ERROR: Request was cancelled (-999) - not retrying to avoid infinite loop") 966 | return 967 | } 968 | 969 | // Check for WebKit frame load interrupted (code 102) 970 | // This usually happens when multiple rapid navigations occur 971 | if let nsError = error as NSError?, 972 | nsError.domain == "WebKitErrorDomain" && nsError.code == 102 { 973 | print("ERROR: Frame load interrupted (102) - not retrying to avoid conflicts") 974 | return 975 | } 976 | 977 | // Only retry for legitimate network/loading errors 978 | DispatchQueue.main.asyncAfter(deadline: .now() + retryTimer) { 979 | print("trying page load... again") 980 | // Show loading indicator when retrying 981 | self.showLoadingIndicator() 982 | webView.load(webView.url != nil ? URLRequest(url: webView.url!) : URLRequest(url: self.config.displayURL)) 983 | } 984 | } 985 | 986 | // version 2.7 - add support for tab/pop-up redirection to webview 987 | func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? { 988 | print("createWebViewWith") 989 | 990 | if config.redirect == "ON" { 991 | print("redirection to existing webview") 992 | if navigationAction.targetFrame == nil { 993 | webView.load(navigationAction.request) 994 | } 995 | } 996 | 997 | if config.redirect == "ALT" { 998 | print("redirection to new webview") 999 | if navigationAction.targetFrame?.isMainFrame != true { 1000 | let newWebView = WKWebView(frame: webView.frame, 1001 | configuration: configuration) 1002 | newWebView.load(navigationAction.request) 1003 | newWebView.uiDelegate = self 1004 | newWebView.navigationDelegate = self 1005 | webView.superview?.addSubview(newWebView) 1006 | 1007 | // Keep track of this webView 1008 | additionalWebViews.append(newWebView) 1009 | 1010 | return newWebView 1011 | } 1012 | } 1013 | 1014 | return nil 1015 | } 1016 | 1017 | // version 2.8.1 - add option to bypass secure SSL 1018 | // version 2.8.2 - fixed crash 1019 | func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { 1020 | if config.disabletrust == "OFF" && challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { 1021 | completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!) ) 1022 | } else { 1023 | completionHandler(URLSession.AuthChallengeDisposition.performDefaultHandling, nil ) 1024 | } 1025 | } 1026 | 1027 | // MARK: - User-activity detection (touch / pan / tap anywhere in the view) 1028 | // version 2.8.5 1029 | private func addUserActivityDetection() { 1030 | // We add recognisers only once. 1031 | guard view.gestureRecognizers?.contains(where: { $0.name == "userActivity" }) != true else { return } 1032 | 1033 | let tap = UITapGestureRecognizer(target: self, action: #selector(userDidInteract)) 1034 | tap.cancelsTouchesInView = false 1035 | tap.name = "userActivity" 1036 | tap.delegate = self 1037 | 1038 | let pan = UIPanGestureRecognizer(target: self, action: #selector(userDidInteract)) 1039 | pan.cancelsTouchesInView = false 1040 | pan.name = "userActivity" 1041 | pan.delegate = self 1042 | 1043 | view.addGestureRecognizer(tap) 1044 | view.addGestureRecognizer(pan) 1045 | } 1046 | 1047 | @objc private func userDidInteract() { 1048 | print("User interaction detected") 1049 | // Reset timers or handle "active use" here as required. 1050 | if config.detectScroll == "ON", config.resetTimer != 0 { 1051 | timer?.invalidate() 1052 | 1053 | // Check if we should start timer based on current URL and resetTimerOnHome setting 1054 | let shouldStartTimer: Bool 1055 | if config.resetTimerOnHome == "ON" { 1056 | // When resetTimerOnHome is ON, always start the timer regardless of URL 1057 | shouldStartTimer = true 1058 | } else { 1059 | // Default behavior: only start timer when NOT at home URL 1060 | shouldStartTimer = (webView?.url != config.homeURL) 1061 | } 1062 | 1063 | if shouldStartTimer { 1064 | timer = Timer.scheduledTimer(timeInterval: TimeInterval(config.resetTimer), 1065 | target: self, 1066 | selector: #selector(fireTimer), 1067 | userInfo: nil, 1068 | repeats: false) 1069 | } 1070 | } 1071 | } 1072 | 1073 | // Allow our gesture recognisers to work alongside the web view's own recognisers. 1074 | func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, 1075 | shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { 1076 | return true 1077 | } 1078 | 1079 | // MARK: - Device Lock Detection 1080 | private func addDeviceLockDetection() { 1081 | // Listen for device lock events 1082 | NotificationCenter.default.addObserver( 1083 | self, 1084 | selector: #selector(deviceDidLock), 1085 | name: UIApplication.didEnterBackgroundNotification, 1086 | object: nil 1087 | ) 1088 | 1089 | NotificationCenter.default.addObserver( 1090 | self, 1091 | selector: #selector(deviceDidUnlock), 1092 | name: UIApplication.didBecomeActiveNotification, 1093 | object: nil 1094 | ) 1095 | 1096 | NotificationCenter.default.addObserver( 1097 | self, 1098 | selector: #selector(deviceWillLock), 1099 | name: UIApplication.willResignActiveNotification, 1100 | object: nil 1101 | ) 1102 | } 1103 | 1104 | @objc private func deviceWillLock() { 1105 | print("Device will lock - preparing for lock state") 1106 | // Pause any ongoing operations, timers, etc. 1107 | timer?.invalidate() 1108 | webView?.stopLoading() 1109 | } 1110 | 1111 | @objc private func deviceDidLock() { 1112 | print("Device locked - app entered background") 1113 | // Additional cleanup when device is locked 1114 | // This could trigger session reset, clear sensitive data, etc. 1115 | if config.resetTimer != 0 { 1116 | // Reset session immediately on device lock if timer is enabled 1117 | DispatchQueue.main.async { 1118 | self.resetSession() 1119 | } 1120 | } 1121 | } 1122 | 1123 | @objc private func deviceDidUnlock() { 1124 | print("Device unlocked - app became active") 1125 | 1126 | } 1127 | 1128 | // Clean up when view controller is deallocated 1129 | deinit { 1130 | // Remove device lock observers 1131 | NotificationCenter.default.removeObserver(self, name: UIApplication.didEnterBackgroundNotification, object: nil) 1132 | NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil) 1133 | NotificationCenter.default.removeObserver(self, name: UIApplication.willResignActiveNotification, object: nil) 1134 | webView?.removeFromSuperview() 1135 | webView = nil 1136 | } 1137 | } 1138 | 1139 | // version 2.8 1140 | // the following snippet is from https://stackoverflow.com/questions/25607247/how-do-i-decode-html-entities-in-swift/30141700#30141700 1141 | 1142 | private let characterEntities : [ Substring : Character ] = [ 1143 | // XML predefined entities: 1144 | """ : "\"", 1145 | "&" : "&", 1146 | "'" : "'", 1147 | "<" : "<", 1148 | ">" : ">", 1149 | 1150 | // HTML character entity references: 1151 | " " : "\u{00a0}", 1152 | // ... 1153 | "♦" : "♦", 1154 | ] 1155 | 1156 | extension String { 1157 | var stringByDecodingHTMLEntities : String { 1158 | func decodeNumeric(_ string : Substring, base : Int) -> Character? { 1159 | guard let code = UInt32(string, radix: base), 1160 | let uniScalar = UnicodeScalar(code) else { return nil } 1161 | return Character(uniScalar) 1162 | } 1163 | 1164 | func decode(_ entity : Substring) -> Character? { 1165 | if entity.hasPrefix("&#x") || entity.hasPrefix("&#X") { 1166 | return decodeNumeric(entity.dropFirst(3).dropLast(), base: 16) 1167 | } else if entity.hasPrefix("&#") { 1168 | return decodeNumeric(entity.dropFirst(2).dropLast(), base: 10) 1169 | } else { 1170 | return characterEntities[entity] 1171 | } 1172 | } 1173 | 1174 | var result = "" 1175 | var position = startIndex 1176 | 1177 | // Find the next '&' and copy the characters preceding it to `result`: 1178 | while let ampRange = self[position...].range(of: "&") { 1179 | result.append(contentsOf: self[position ..< ampRange.lowerBound]) 1180 | position = ampRange.lowerBound 1181 | 1182 | // Find the next ';' and copy everything from '&' to ';' into `entity` 1183 | guard let semiRange = self[position...].range(of: ";") else { 1184 | // No matching ';'. 1185 | break 1186 | } 1187 | let entity = self[position ..< semiRange.upperBound] 1188 | position = semiRange.upperBound 1189 | 1190 | if let decoded = decode(entity) { 1191 | // Replace by decoded character: 1192 | result.append(decoded) 1193 | } else { 1194 | // Invalid entity, copy verbatim: 1195 | result.append(contentsOf: entity) 1196 | } 1197 | } 1198 | // Copy remaining characters to `result`: 1199 | result.append(contentsOf: self[position...]) 1200 | return result 1201 | } 1202 | } 1203 | --------------------------------------------------------------------------------