diff --git a/.idea/capacitor-fused-location.iml b/.idea/capacitor-fused-location.iml new file mode 100644 index 0000000..24643cc --- /dev/null +++ b/.idea/capacitor-fused-location.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..28a804d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..67496b3 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..37a97ea --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/CapacitorFusedLocation.podspec b/CapacitorFusedLocation.podspec new file mode 100644 index 0000000..048842d --- /dev/null +++ b/CapacitorFusedLocation.podspec @@ -0,0 +1,13 @@ + + Pod::Spec.new do |s| + s.name = 'CapacitorFusedLocation' + s.version = '0.0.1' + s.summary = 'Provides acces to the fused location api by google play services' + s.license = 'MIT' + s.homepage = 'https://github.com/jonoj-team/capacitor-fused-location' + s.author = 'Johannes Normann Jensen' + s.source = { :git => 'https://github.com/jonoj-team/capacitor-fused-location', :tag => s.version.to_s } + s.source_files = 'ios/Plugin/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}' + s.ios.deployment_target = '11.0' + s.dependency 'Capacitor' + end \ No newline at end of file diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml new file mode 100644 index 0000000..7ac24c7 --- /dev/null +++ b/android/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/android/.idea/misc.xml b/android/.idea/misc.xml new file mode 100644 index 0000000..a76efc8 --- /dev/null +++ b/android/.idea/misc.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + 1.8 + + + + + + + + \ No newline at end of file diff --git a/android/.idea/modules.xml b/android/.idea/modules.xml new file mode 100644 index 0000000..abc31fc --- /dev/null +++ b/android/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/runConfigurations.xml b/android/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/android/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/android/.npmignore b/android/.npmignore new file mode 100644 index 0000000..39fb081 --- /dev/null +++ b/android/.npmignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/android/capacitor-fused-location/.npmignore b/android/capacitor-fused-location/.npmignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/android/capacitor-fused-location/.npmignore @@ -0,0 +1 @@ +/build diff --git a/android/capacitor-fused-location/build.gradle b/android/capacitor-fused-location/build.gradle new file mode 100644 index 0000000..8fdb2c1 --- /dev/null +++ b/android/capacitor-fused-location/build.gradle @@ -0,0 +1,50 @@ +buildscript { + repositories { + jcenter() + google() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.1.1' + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 27 + defaultConfig { + minSdkVersion 21 + targetSdkVersion 27 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } +} + +repositories { + google() + jcenter() + mavenCentral() + maven { + url "https://dl.bintray.com/ionic-team/capacitor" + } +} + + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'ionic-team:capacitor-android:1+' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.1' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' +} + diff --git a/android/capacitor-fused-location/proguard-rules.pro b/android/capacitor-fused-location/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/android/capacitor-fused-location/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/android/capacitor-fused-location/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java b/android/capacitor-fused-location/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java new file mode 100644 index 0000000..1d58c77 --- /dev/null +++ b/android/capacitor-fused-location/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.getcapacitor.android; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.getcapacitor.android", appContext.getPackageName()); + } +} diff --git a/android/capacitor-fused-location/src/main/AndroidManifest.xml b/android/capacitor-fused-location/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1573f70 --- /dev/null +++ b/android/capacitor-fused-location/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/android/capacitor-fused-location/src/main/java/com/jonoj/plugin/FusedLocation.java b/android/capacitor-fused-location/src/main/java/com/jonoj/plugin/FusedLocation.java new file mode 100644 index 0000000..e66448a --- /dev/null +++ b/android/capacitor-fused-location/src/main/java/com/jonoj/plugin/FusedLocation.java @@ -0,0 +1,20 @@ +package com.jonoj.plugin; + +import com.getcapacitor.JSObject; +import com.getcapacitor.NativePlugin; +import com.getcapacitor.Plugin; +import com.getcapacitor.PluginCall; +import com.getcapacitor.PluginMethod; + +@NativePlugin() +public class FusedLocation extends Plugin { + + @PluginMethod() + public void echo(PluginCall call) { + String value = call.getString("value"); + + JSObject ret = new JSObject(); + ret.put("value", value); + call.success(ret); + } +} diff --git a/android/capacitor-fused-location/src/main/res/layout/bridge_layout_main.xml b/android/capacitor-fused-location/src/main/res/layout/bridge_layout_main.xml new file mode 100644 index 0000000..b69e589 --- /dev/null +++ b/android/capacitor-fused-location/src/main/res/layout/bridge_layout_main.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/android/capacitor-fused-location/src/main/res/values/colors.xml b/android/capacitor-fused-location/src/main/res/values/colors.xml new file mode 100644 index 0000000..045e125 --- /dev/null +++ b/android/capacitor-fused-location/src/main/res/values/colors.xml @@ -0,0 +1,3 @@ + + + diff --git a/android/capacitor-fused-location/src/main/res/values/strings.xml b/android/capacitor-fused-location/src/main/res/values/strings.xml new file mode 100644 index 0000000..6789e13 --- /dev/null +++ b/android/capacitor-fused-location/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Just a simple string + diff --git a/android/capacitor-fused-location/src/main/res/values/styles.xml b/android/capacitor-fused-location/src/main/res/values/styles.xml new file mode 100644 index 0000000..f11f745 --- /dev/null +++ b/android/capacitor-fused-location/src/main/res/values/styles.xml @@ -0,0 +1,3 @@ + + + diff --git a/android/capacitor-fused-location/src/test/java/com/getcapacitor/ExampleUnitTest.java b/android/capacitor-fused-location/src/test/java/com/getcapacitor/ExampleUnitTest.java new file mode 100644 index 0000000..06806a7 --- /dev/null +++ b/android/capacitor-fused-location/src/test/java/com/getcapacitor/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.getcapacitor; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..aac7c9b --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,17 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..0cfd5f2 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Dec 01 12:41:00 CST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/android/gradlew b/android/gradlew new file mode 100755 index 0000000..9d82f78 --- /dev/null +++ b/android/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/scripts/release.sh b/android/scripts/release.sh new file mode 100644 index 0000000..e69de29 diff --git a/ios/Plugin/.DS_Store b/ios/Plugin/.DS_Store new file mode 100644 index 0000000..89891a3 Binary files /dev/null and b/ios/Plugin/.DS_Store differ diff --git a/ios/Plugin/Plugin.xcodeproj/project.pbxproj b/ios/Plugin/Plugin.xcodeproj/project.pbxproj new file mode 100644 index 0000000..4d13666 --- /dev/null +++ b/ios/Plugin/Plugin.xcodeproj/project.pbxproj @@ -0,0 +1,587 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */; }; + 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */; }; + 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFF88201F53D600D50D53 /* Plugin.framework */; }; + 50ADFF97201F53D600D50D53 /* PluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFF96201F53D600D50D53 /* PluginTests.swift */; }; + 50ADFF99201F53D600D50D53 /* Plugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 50ADFF8B201F53D600D50D53 /* Plugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFFA52020D75100D50D53 /* Capacitor.framework */; }; + 50ADFFA82020EE4F00D50D53 /* Plugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFFA72020EE4F00D50D53 /* Plugin.m */; }; + 50E1A94820377CB70090CE1A /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E1A94720377CB70090CE1A /* Plugin.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 50ADFF93201F53D600D50D53 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 50ADFF7F201F53D600D50D53 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 50ADFF87201F53D600D50D53; + remoteInfo = Plugin; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFF88201F53D600D50D53 /* Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFF8B201F53D600D50D53 /* Plugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Plugin.h; sourceTree = ""; }; + 50ADFF8C201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 50ADFF91201F53D600D50D53 /* PluginTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PluginTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFF96201F53D600D50D53 /* PluginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginTests.swift; sourceTree = ""; }; + 50ADFF98201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 50ADFFA52020D75100D50D53 /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFFA72020EE4F00D50D53 /* Plugin.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Plugin.m; sourceTree = ""; }; + 50E1A94720377CB70090CE1A /* Plugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Plugin.swift; sourceTree = ""; }; + 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.debug.xcconfig"; sourceTree = ""; }; + 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.release.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.release.xcconfig"; sourceTree = ""; }; + 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.debug.xcconfig"; sourceTree = ""; }; + F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.release.xcconfig"; sourceTree = ""; }; + F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PluginTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 50ADFF84201F53D600D50D53 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */, + 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8E201F53D600D50D53 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */, + 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 50ADFF7E201F53D600D50D53 = { + isa = PBXGroup; + children = ( + 50ADFF8A201F53D600D50D53 /* Plugin */, + 50ADFF95201F53D600D50D53 /* PluginTests */, + 50ADFF89201F53D600D50D53 /* Products */, + 8C8E7744173064A9F6D438E3 /* Pods */, + A797B9EFA3DCEFEA1FBB66A9 /* Frameworks */, + ); + sourceTree = ""; + }; + 50ADFF89201F53D600D50D53 /* Products */ = { + isa = PBXGroup; + children = ( + 50ADFF88201F53D600D50D53 /* Plugin.framework */, + 50ADFF91201F53D600D50D53 /* PluginTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 50ADFF8A201F53D600D50D53 /* Plugin */ = { + isa = PBXGroup; + children = ( + 50E1A94720377CB70090CE1A /* Plugin.swift */, + 50ADFF8B201F53D600D50D53 /* Plugin.h */, + 50ADFFA72020EE4F00D50D53 /* Plugin.m */, + 50ADFF8C201F53D600D50D53 /* Info.plist */, + ); + path = Plugin; + sourceTree = ""; + }; + 50ADFF95201F53D600D50D53 /* PluginTests */ = { + isa = PBXGroup; + children = ( + 50ADFF96201F53D600D50D53 /* PluginTests.swift */, + 50ADFF98201F53D600D50D53 /* Info.plist */, + ); + path = PluginTests; + sourceTree = ""; + }; + 8C8E7744173064A9F6D438E3 /* Pods */ = { + isa = PBXGroup; + children = ( + 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */, + 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */, + 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */, + F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + A797B9EFA3DCEFEA1FBB66A9 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 50ADFFA52020D75100D50D53 /* Capacitor.framework */, + 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */, + F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 50ADFF85201F53D600D50D53 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF99201F53D600D50D53 /* Plugin.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 50ADFF87201F53D600D50D53 /* Plugin */ = { + isa = PBXNativeTarget; + buildConfigurationList = 50ADFF9C201F53D600D50D53 /* Build configuration list for PBXNativeTarget "Plugin" */; + buildPhases = ( + AB5B3E54B4E897F32C2279DA /* [CP] Check Pods Manifest.lock */, + 50ADFF83201F53D600D50D53 /* Sources */, + 50ADFF84201F53D600D50D53 /* Frameworks */, + 50ADFF85201F53D600D50D53 /* Headers */, + 50ADFF86201F53D600D50D53 /* Resources */, + AE646EB3107D841B880D174A /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Plugin; + productName = Plugin; + productReference = 50ADFF88201F53D600D50D53 /* Plugin.framework */; + productType = "com.apple.product-type.framework"; + }; + 50ADFF90201F53D600D50D53 /* PluginTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 50ADFF9F201F53D600D50D53 /* Build configuration list for PBXNativeTarget "PluginTests" */; + buildPhases = ( + 0596884F929ED6F1DE134961 /* [CP] Check Pods Manifest.lock */, + 50ADFF8D201F53D600D50D53 /* Sources */, + 50ADFF8E201F53D600D50D53 /* Frameworks */, + 50ADFF8F201F53D600D50D53 /* Resources */, + CCA81D3B7E26D0D727D24C84 /* [CP] Embed Pods Frameworks */, + 32BFB60F8ADE8D433EDE204C /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 50ADFF94201F53D600D50D53 /* PBXTargetDependency */, + ); + name = PluginTests; + productName = PluginTests; + productReference = 50ADFF91201F53D600D50D53 /* PluginTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 50ADFF7F201F53D600D50D53 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = "Max Lynch"; + TargetAttributes = { + 50ADFF87201F53D600D50D53 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 0920; + ProvisioningStyle = Automatic; + }; + 50ADFF90201F53D600D50D53 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 50ADFF82201F53D600D50D53 /* Build configuration list for PBXProject "Plugin" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 50ADFF7E201F53D600D50D53; + productRefGroup = 50ADFF89201F53D600D50D53 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 50ADFF87201F53D600D50D53 /* Plugin */, + 50ADFF90201F53D600D50D53 /* PluginTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 50ADFF86201F53D600D50D53 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8F201F53D600D50D53 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 0596884F929ED6F1DE134961 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-PluginTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 32BFB60F8ADE8D433EDE204C /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + AB5B3E54B4E897F32C2279DA /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Plugin-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + AE646EB3107D841B880D174A /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + CCA81D3B7E26D0D727D24C84 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Capacitor/Capacitor.framework", + "${BUILT_PRODUCTS_DIR}/CapacitorCordova/Cordova.framework", + "${BUILT_PRODUCTS_DIR}/GCDWebServer/GCDWebServer.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Capacitor.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cordova.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GCDWebServer.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 50ADFF83201F53D600D50D53 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 50E1A94820377CB70090CE1A /* Plugin.swift in Sources */, + 50ADFFA82020EE4F00D50D53 /* Plugin.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8D201F53D600D50D53 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF97201F53D600D50D53 /* PluginTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 50ADFF94201F53D600D50D53 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 50ADFF87201F53D600D50D53 /* Plugin */; + targetProxy = 50ADFF93201F53D600D50D53 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 50ADFF9A201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 50ADFF9B201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 50ADFF9D201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Plugin/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)"; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 50ADFF9E201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Plugin/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)"; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 50ADFFA0201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PluginTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.PluginTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 50ADFFA1201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PluginTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.PluginTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 50ADFF82201F53D600D50D53 /* Build configuration list for PBXProject "Plugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFF9A201F53D600D50D53 /* Debug */, + 50ADFF9B201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 50ADFF9C201F53D600D50D53 /* Build configuration list for PBXNativeTarget "Plugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFF9D201F53D600D50D53 /* Debug */, + 50ADFF9E201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 50ADFF9F201F53D600D50D53 /* Build configuration list for PBXNativeTarget "PluginTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFFA0201F53D600D50D53 /* Debug */, + 50ADFFA1201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 50ADFF7F201F53D600D50D53 /* Project object */; +} diff --git a/ios/Plugin/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Plugin/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..23dacd5 --- /dev/null +++ b/ios/Plugin/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Plugin/Plugin.xcodeproj/project.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate b/ios/Plugin/Plugin.xcodeproj/project.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..7be5023 Binary files /dev/null and b/ios/Plugin/Plugin.xcodeproj/project.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/ios/Plugin/Plugin.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/xcschememanagement.plist b/ios/Plugin/Plugin.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..28229a6 --- /dev/null +++ b/ios/Plugin/Plugin.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + Plugin.xcscheme + + orderHint + 5 + + + + diff --git a/ios/Plugin/Plugin.xcworkspace/contents.xcworkspacedata b/ios/Plugin/Plugin.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..afad624 --- /dev/null +++ b/ios/Plugin/Plugin.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/ios/Plugin/Plugin.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate b/ios/Plugin/Plugin.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..2df9265 Binary files /dev/null and b/ios/Plugin/Plugin.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/ios/Plugin/Plugin.xcworkspace/xcuserdata/max.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/ios/Plugin/Plugin.xcworkspace/xcuserdata/max.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..08b6dd6 --- /dev/null +++ b/ios/Plugin/Plugin.xcworkspace/xcuserdata/max.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/ios/Plugin/Plugin/Info.plist b/ios/Plugin/Plugin/Info.plist new file mode 100644 index 0000000..1007fd9 --- /dev/null +++ b/ios/Plugin/Plugin/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/ios/Plugin/Plugin/Plugin.h b/ios/Plugin/Plugin/Plugin.h new file mode 100644 index 0000000..f2bd9e0 --- /dev/null +++ b/ios/Plugin/Plugin/Plugin.h @@ -0,0 +1,10 @@ +#import + +//! Project version number for Plugin. +FOUNDATION_EXPORT double PluginVersionNumber; + +//! Project version string for Plugin. +FOUNDATION_EXPORT const unsigned char PluginVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + diff --git a/ios/Plugin/Plugin/Plugin.m b/ios/Plugin/Plugin/Plugin.m new file mode 100644 index 0000000..023437a --- /dev/null +++ b/ios/Plugin/Plugin/Plugin.m @@ -0,0 +1,8 @@ +#import +#import + +// Define the plugin using the CAP_PLUGIN Macro, and +// each method the plugin supports using the CAP_PLUGIN_METHOD macro. +CAP_PLUGIN(FusedLocation, "FusedLocation", + CAP_PLUGIN_METHOD(echo, CAPPluginReturnPromise); +) diff --git a/ios/Plugin/Plugin/Plugin.swift b/ios/Plugin/Plugin/Plugin.swift new file mode 100644 index 0000000..58a6be7 --- /dev/null +++ b/ios/Plugin/Plugin/Plugin.swift @@ -0,0 +1,17 @@ +import Foundation +import Capacitor + +/** + * Please read the Capacitor iOS Plugin Development Guide + * here: https://capacitor.ionicframework.com/docs/plugins/ios + */ +@objc(FusedLocation) +public class FusedLocation: CAPPlugin { + + @objc func echo(_ call: CAPPluginCall) { + let value = call.getString("value") ?? "" + call.success([ + "value": value + ]) + } +} diff --git a/ios/Plugin/PluginTests/Info.plist b/ios/Plugin/PluginTests/Info.plist new file mode 100644 index 0000000..6c40a6c --- /dev/null +++ b/ios/Plugin/PluginTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/ios/Plugin/PluginTests/PluginTests.swift b/ios/Plugin/PluginTests/PluginTests.swift new file mode 100644 index 0000000..00f036e --- /dev/null +++ b/ios/Plugin/PluginTests/PluginTests.swift @@ -0,0 +1,35 @@ +import XCTest +import Capacitor +@testable import Plugin + +class PluginTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testEcho() { + // This is an example of a functional test case for a plugin. + // Use XCTAssert and related functions to verify your tests produce the correct results. + + let value = "Hello, World!" + let plugin = MyPlugin() + + let call = CAPPluginCall(callbackId: "test", options: [ + "value": value + ], success: { (result, call) in + let resultValue = result!.data["value"] as? String + XCTAssertEqual(value, resultValue) + }, error: { (err) in + XCTFail("Error shouldn't have been called") + }) + + plugin.echo(call!) + } +} diff --git a/ios/Plugin/Podfile b/ios/Plugin/Podfile new file mode 100644 index 0000000..2c5b62c --- /dev/null +++ b/ios/Plugin/Podfile @@ -0,0 +1,16 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '11.0' + +target 'Plugin' do + # Comment the next line if you're not using Swift and don't want to use dynamic frameworks + use_frameworks! + + # Pods for IonicRunner + pod 'Capacitor' +end + +target 'PluginTests' do + use_frameworks! + + pod 'Capacitor' +end \ No newline at end of file diff --git a/ios/Plugin/Podfile.lock b/ios/Plugin/Podfile.lock new file mode 100644 index 0000000..487d066 --- /dev/null +++ b/ios/Plugin/Podfile.lock @@ -0,0 +1,20 @@ +PODS: + - Capacitor (0.0.8): + - CapacitorCordova (= 0.0.8) + - GCDWebServer (~> 3.0) + - CapacitorCordova (0.0.8) + - GCDWebServer (3.4.2): + - GCDWebServer/Core (= 3.4.2) + - GCDWebServer/Core (3.4.2) + +DEPENDENCIES: + - Capacitor + +SPEC CHECKSUMS: + Capacitor: 36a275e2fd85b69a2cb9633e8920ecf5c5aab88d + CapacitorCordova: cf26a9b075eb1f339118c920a975e4ff9e403193 + GCDWebServer: 8d67ee9f634b4bb91eb4b8aee440318a5fc6debd + +PODFILE CHECKSUM: 57e6d463d83fe9e2081ca17be52456b12c5e148c + +COCOAPODS: 1.4.0 diff --git a/ios/Plugin/Pods/Capacitor/LICENSE b/ios/Plugin/Pods/Capacitor/LICENSE new file mode 100644 index 0000000..623c70a --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/LICENSE @@ -0,0 +1,23 @@ +Copyright 2015-present Drifty Co. +http://drifty.com/ + +MIT License + +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: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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. diff --git a/ios/Plugin/Pods/Capacitor/README.md b/ios/Plugin/Pods/Capacitor/README.md new file mode 100644 index 0000000..3710844 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/README.md @@ -0,0 +1,44 @@ +*Capacitor is being actively developed and is not currently ready for public use. See our [Timeline](#timeline) for our upcoming plans and tentatively timeline* + +# ⚡️ Capacitor: Cross-platform apps with JavaScript and the Web ⚡️ + +Capacitor is a cross-platform API and code execution layer that makes it easy to call Native SDKs from web code and to write custom Native plugins that your app might need. Additionally, Capacitor provides first-class Progressive Web App support so you can write one app and deploy it to the app stores, _and_ the mobile web. + +Capacitor is being designed by the Ionic Framework team as an eventual alternative to Cordova, though backwards compatibility with Cordova plugins is a priority and is actively being worked on. Capacitor can be used without Ionic Framework, but soon it'll become a core part of the Ionic developer experience. + +Capacitor also comes with a Plugin API for building native plugins. On iOS, first-class Swift support is available, and much of the iOS Capacitor runtime is written in Swift. Plugins may also be written in Objective-C. On Android, support for writing plugins with Java and Kotlin is supported. + +Capacitor is still a work in progress and is not quite ready for use. Stay tuned for a public release in early 2018. + +## Timeline + +_Disclaimer: These dates are tentative. "It'll be ready when it's ready!"_ + +*Short term milestones* + + - November 2017 - Project Start + - January/Feb 2018 - Private alpha testing + - Feb 2018 - Public alpha + +## Roadmap + +_Disclaimer: Our roadmap is subject to change at any time and has no specific date guarantees_ + +2018 + + - __Cordova Plugin Integration__ + - Preliminary support for using plugins from the existing Cordova community + - __Native Shell Add-ons__ + - Support for interacting with Native UI shell elements, such as native menus, tabs, and navigation, with 1-1 fallbacks to the web for first-class Progressive Web App and Electron support. + - __Electron support__ + - Support for building Electron apps and interacting with Node.js libraries + - __Enterprise Premium Plugins__ + - Paid add-on plugins with support for common Enterprise use cases, such as storage, authentication, security, and more + - Developer Support options with SLAs and priority patches + - We are working with a few large teams/businesses as early development partners. Interested? Email [max@ionicframework.com](mailto:max@ionicframework.com) + +## Contributing + +Contributing to Capacitor may involve writing TypeScript, Swift/Obective-C, Java, or Markdown depending on the component you are working on. We are looking or help in any of those areas! + +Please read the [Contributing](.github/CONTRIBUTING.md) guide for more information. diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPBridge.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPBridge.swift new file mode 100644 index 0000000..72e0543 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPBridge.swift @@ -0,0 +1,448 @@ +import Foundation +import Dispatch +import WebKit +import Cordova + +enum BridgeError: Error { + case errorExportingCoreJS +} + +@objc public class CAPBridge : NSObject { + public static var CAP_SITE = "https://getcapacitor.com/" + + public var userContentController: WKUserContentController + @objc public var viewController: UIViewController + + public var lastPlugin: CAPPlugin? + + // Map of all loaded and instantiated plugins by pluginId -> instance + public var plugins = [String:CAPPlugin]() + // List of known plugins by pluginId -> Plugin Type + public var knownPlugins = [String:CAPPlugin.Type]() + + public var cordovaPlugins = [String:CDVPlugin]() + + public var storedCalls = [String:CAPPluginCall]() + + private var isActive = true + + private var jsVerboseModeEnabled = true + + // Dispatch queue for our operations + // TODO: Unique label? + public var dispatchQueue = DispatchQueue(label: "bridge") + + public init(_ vc: UIViewController, _ userContentController: WKUserContentController) { + self.viewController = vc + self.userContentController = userContentController + super.init() + exportCoreJS() + setupCordovaCompatibility() + registerPlugins() + bindObservers() + } + + public func willAppear() { + if let splash = getOrLoadPlugin(pluginId: "SplashScreen") as? SplashScreen { + splash.showOnLaunch() + } + } + + public static func handleOpenUrl(_ url: URL, _ options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool { + NotificationCenter.default.post(name: Notification.Name(CAPNotifications.URLOpen.name()), object: [ + "url": url, + "options": options + ]) + return true + } + + /** + * Handle continueUserActivity, for now this just provides universal link responding support. + */ + public static func handleContinueActivity(_ userActivity: NSUserActivity, _ restorationHandler: @escaping ([Any]?) -> Void) -> Bool { + // TODO: Support other types, emit to rest of plugins + if userActivity.activityType != NSUserActivityTypeBrowsingWeb || userActivity.webpageURL == nil { + return false + } + + let url = userActivity.webpageURL + + NotificationCenter.default.post(name: Notification.Name(CAPNotifications.UniversalLinkOpen.name()), object: [ + "url": url + ]) + return true + } + + public static func handleAppBecameActive(_ application: UIApplication) { + // no-op for now + } + + static func fatalError(_ error: Error, _ originalError: Error) { + print("⚡️ ❌ Capacitor: FATAL ERROR") + print("⚡️ ❌ Error was: ", originalError.localizedDescription) + switch error { + case BridgeError.errorExportingCoreJS: + print("⚡️ ❌ Unable to export required Bridge JavaScript. Bridge will not function.") + if let wke = originalError as? WKError { + print("⚡️ ❌ ", wke.userInfo) + } + default: + print("⚡️ ❌ Unknown error") + } + + print("⚡️ ❌ Please verify your installation or file an issue") + } + + func bindObservers() { + let appStatePlugin = getOrLoadPlugin(pluginId: "App") as? App + + NotificationCenter.default.addObserver(forName: .UIApplicationDidBecomeActive, object: nil, queue: OperationQueue.main) { (notification) in + print("APP ACTIVE") + self.isActive = true + appStatePlugin?.fireChange(isActive: self.isActive) + } + NotificationCenter.default.addObserver(forName: .UIApplicationDidEnterBackground, object: nil, queue: OperationQueue.main) { (notification) in + print("APP INACTIVE") + self.isActive = false + appStatePlugin?.fireChange(isActive: self.isActive) + } + } + + func isAppActive() -> Bool { + return isActive + } + + func exportCoreJS() { + do { + try JSExport.exportCapacitorJS(userContentController: self.userContentController) + } catch { + CAPBridge.fatalError(error, error) + } + } + + func setupCordovaCompatibility() { + var injectCordovaFiles = false + var numClasses = UInt32(0); + let classes = objc_copyClassList(&numClasses) + for i in 0.. CAPPlugin? { + guard let plugin = self.getPlugin(pluginId: pluginId) ?? self.loadPlugin(pluginId: pluginId) else { + return nil + } + return plugin + } + + public func getPlugin(pluginId: String) -> CAPPlugin? { + return self.plugins[pluginId] + } + + public func loadPlugin(pluginId: String) -> CAPPlugin? { + guard let pluginType = knownPlugins[pluginId] else { + print("⚡️ Unable to load plugin \(pluginId). No such module found.") + return nil + } + + let bridgeType = pluginType as! CAPBridgedPlugin.Type + let p = pluginType.init(bridge: self, pluginId: bridgeType.pluginId()) + p!.load() + self.plugins[bridgeType.pluginId()] = p + return p + } + + func savePluginCall(_ call: CAPPluginCall) { + storedCalls[call.callbackId] = call + } + + @objc public func getSavedCall(_ callbackId: String) -> CAPPluginCall? { + return storedCalls[callbackId] + } + + @objc public func releaseCall(_ call: CAPPluginCall) { + storedCalls.removeValue(forKey: call.callbackId) + } + + @objc public func releaseCall(callbackId: String) { + storedCalls.removeValue(forKey: callbackId) + } + + public func getDispatchQueue() -> DispatchQueue { + return self.dispatchQueue + } + + func registerCordovaPlugins() { + do { + try JSExport.exportCordovaPluginsJS(userContentController: self.userContentController) + } catch { + CAPBridge.fatalError(error, error) + } + } + + public func isSimulator() -> Bool { + var isSimulator = false + #if arch(i386) || arch(x86_64) + isSimulator = true + #endif + return isSimulator + } + + public func isDevMode() -> Bool { + #if DEBUG + return true + #else + return false + #endif + } + + public func showDevMode() { + let devMode = DevMode(self) + devMode.show() + } + + public func reload() { + self.getWebView().reload() + } + + + public func modulePrint(_ plugin: CAPPlugin, _ items: Any...) { + let output = items.map { "\($0)" }.joined(separator: " ") + Swift.print("⚡️ ", plugin.pluginId, "-", output) + } + + public func alert(_ title: String, _ message: String, _ buttonTitle: String = "OK") { + let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert) + alert.addAction(UIAlertAction(title: buttonTitle, style: UIAlertActionStyle.default, handler: nil)) + self.viewController.present(alert, animated: true, completion: nil) + } + + func docLink(_ url: String) -> String { + return "\(CAPBridge.CAP_SITE)/docs/\(url)" + } + + /** + * Handle a call from JavaScript. First, find the corresponding plugin, + * construct a selector, and perform that selector on the plugin instance. + */ + public func handleJSCall(call: JSCall) { + guard let plugin = self.getPlugin(pluginId: call.pluginId) ?? self.loadPlugin(pluginId: call.pluginId) else { + print("⚡️ Error loading plugin \(call.pluginId) for call. Check that the pluginId is correct") + return + } + guard let pluginType = knownPlugins[plugin.getId()] else { + return + } + + var selector: Selector? = nil + if call.method == "addListener" || call.method == "removeListener" { + selector = NSSelectorFromString(call.method + ":") + } else { + let bridgeType = pluginType as! CAPBridgedPlugin.Type + guard let method = bridgeType.getMethod(call.method) else { + print("⚡️ Error calling method \(call.method) on plugin \(call.pluginId): No method found.") + print("⚡️ Ensure plugin method exists and uses @objc in its declaration, and has been defined") + return + } + + //print("\n⚡️ Calling method \"\(call.method)\" on plugin \"\(plugin.getId()!)\"") + + selector = method.selector + } + + if !plugin.responds(to: selector) { + print("⚡️ Error: Plugin \(plugin.getId()!) does not respond to method call \"\(call.method)\" using selector \"\(selector!)\".") + print("⚡️ Ensure plugin method exists, uses @objc in its declaration, and arguments match selector without callbacks in CAP_PLUGIN_METHOD.") + print("⚡️ Learn more: \(docLink(DocLinks.CAPPluginMethodSelector.rawValue))") + return + } + + // Create a plugin call object and handle the success/error callbacks + dispatchQueue.async { + //let startTime = CFAbsoluteTimeGetCurrent() + + let pluginCall = CAPPluginCall(callbackId: call.callbackId, options: call.options, success: {(result: CAPPluginCallResult?, pluginCall: CAPPluginCall?) -> Void in + if result != nil { + self.toJs(result: JSResult(call: call, result: result!.data ?? [:]), save: pluginCall?.isSaved ?? false) + } else { + self.toJs(result: JSResult(call: call, result: [:]), save: pluginCall?.isSaved ?? false) + } + }, error: {(error: CAPPluginCallError?) -> Void in + let description = error?.error?.localizedDescription ?? "" + self.toJsError(error: JSResultError(call: call, message: error!.message, errorMessage: description, error: error!.data)) + })! + + plugin.perform(selector, with: pluginCall) + + if pluginCall.isSaved { + self.savePluginCall(pluginCall) + } + + //let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime + //print("Native call took", timeElapsed) + } + } + + /** + * Handle a Cordova call from JavaScript. First, find the corresponding plugin, + * construct a selector, and perform that selector on the plugin instance. + */ + public func handleCordovaJSCall(call: JSCall) { + // Create a selector to send to the plugin + var vcClass: CDVPlugin.Type? + let plugin: CDVPlugin + var className: String + if let firstPlugin = cordovaPlugins.first(where: { $0.key == call.pluginId || $0.key == "CDV\(call.pluginId)" }) { + className = firstPlugin.key + plugin = firstPlugin.value + } else { + className = call.pluginId + vcClass = NSClassFromString(call.pluginId) as? CDVPlugin.Type + if vcClass == nil { + className = "CDV\(call.pluginId)" + vcClass = NSClassFromString(className) as? CDVPlugin.Type + } + if vcClass == nil { + print("Error: Plugin class not found") + return + } + plugin = vcClass!.init() + cordovaPlugins[className] = plugin + } + + plugin.viewController = self.viewController + plugin.commandDelegate = CDVCommandDelegateImpl.init(webView: self.getWebView()) + plugin.webView = self.getWebView() as UIView + + let selector = NSSelectorFromString("\(call.method):") + if !plugin.responds(to: selector) { + print("Error: Plugin \(call.pluginId) does not respond to method call \(selector).") + print("Ensure plugin method exists and uses @objc in its declaration") + return + } + + dispatchQueue.sync { + let arguments = call.options["options"] as! [Any] + let pluginCall = CDVInvokedUrlCommand(arguments: arguments, callbackId: call.callbackId, className: className, methodName: call.method) + plugin.perform(selector, with: pluginCall) + } + } + + /** + * Send a successful result to the JavaScript layer. + */ + public func toJs(result: JSResult, save: Bool) { + do { + let resultJson = try result.toJson() + print("⚡️ TO JS", resultJson.prefix(256)) + + DispatchQueue.main.async { + self.getWebView().evaluateJavaScript(""" + window.Capacitor.fromNative({ + callbackId: '\(result.call.callbackId)', + pluginId: '\(result.call.pluginId)', + methodName: '\(result.call.method)', + save: \(save), + success: true, + data: \(resultJson) + }) + """) { (result, error) in + if error != nil && result != nil { + print(result!) + } + } + } + } catch { + if let jsError = error as? JSProcessingError { + let appState = getOrLoadPlugin(pluginId: "App") as! App + + appState.firePluginError(jsError) + } + } + + } + + /** + * Send an error result to the JavaScript layer. + */ + public func toJsError(error: JSResultError) { + DispatchQueue.main.async { + self.getWebView().evaluateJavaScript("window.Capacitor.fromNative({ callbackId: '\(error.call.callbackId)', pluginId: '\(error.call.pluginId)', methodName: '\(error.call.method)', success: false, error: \(error.toJson())})") { (result, error) in + if error != nil && result != nil { + print(result!) + } + } + } + } + + /** + * Eval JS for a specific plugin. + */ + @objc public func evalWithPlugin(_ plugin: CAPPlugin, js: String) { + let wrappedJs = """ + window.Capacitor.withPlugin('\(plugin.getId())', function(plugin) { + if(!plugin) { console.error('Unable to execute JS in plugin, no such plugin found for id \(plugin.getId())'); } + \(js) + }); + """ + + DispatchQueue.main.async { + self.getWebView().evaluateJavaScript(wrappedJs, completionHandler: { (result, error) in + if error != nil { + print("⚡️ JS Eval error", error!.localizedDescription) + } + }) + } + } + + func getWebView() -> WKWebView { + let vc = self.viewController as! CAPBridgeViewController + return vc.getWebView() + } +} + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPBridgeViewController.swift new file mode 100644 index 0000000..f61ca97 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -0,0 +1,304 @@ +// +// ViewController.swift +// IonicRunner +// +// Created by Max Lynch on 3/22/17. +// Copyright © 2017 Max Lynch. All rights reserved. +// + +import UIKit +import WebKit +import GCDWebServer + +class CAPBridgeViewController: UIViewController, WKScriptMessageHandler, WKUIDelegate, WKNavigationDelegate { + + private var webView: WKWebView? + private var webServer: GCDWebServer? + + private var port: Int? + + // Construct the Capacitor runtime + public var bridge: CAPBridge? + + override func loadView() { + let webViewConfiguration = WKWebViewConfiguration() + + let o = WKUserContentController() + o.add(self, name: "bridge") + + webViewConfiguration.userContentController = o + + configureWebView(configuration: webViewConfiguration) + + webView = WKWebView(frame: .zero, configuration: webViewConfiguration) + webView?.scrollView.bounces = false + webView?.uiDelegate = self + webView?.navigationDelegate = self + //If you want to implement the delegate + //webView?.navigationDelegate = self + webView?.configuration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs") + view = webView + + bridge = CAPBridge(self, o) + } + + override func viewDidLoad() { + super.viewDidLoad() + self.becomeFirstResponder() + + loadWebView() + } + + public override func viewWillAppear(_ animated: Bool) { + bridge!.willAppear() + } + + func loadWebView() { + if Bundle.main.path(forResource: "public/index", ofType: "html") == nil { + print("⚡️ FATAL ERROR: Unable to load public/index.html") + print("⚡️ This file is the root of your web app and must exist before") + print("⚡️ Capacitor can run. Ensure you've run capacitor sync at least once") + exit(1) + } + + port = getPort() + + print("⚡️ Starting web server on port \(port!)...") + startWebServer(port: port!) + + print("⚡️ Loading index.html...") + let request = URLRequest(url: URL(string: "http://localhost:\(port!)/")!) + _ = webView?.load(request) + } + + func getPort() -> Int { + let defaults = UserDefaults.standard + var port = defaults.integer(forKey: "capacitorPort") + if port > 0 { + return port + } + port = getRandomPort() + defaults.set(port, forKey: "capacitorPort") + return port + } + + func getRandomPort() -> Int { + let range: [Int] = [3000, 9000] + return range[0] + Int(arc4random_uniform(UInt32(range[1]-range[0]))) + } + + func startWebServer(port: Int) { + let publicPath = Bundle.main.path(forResource: "public", ofType: nil) + GCDWebServer.setLogLevel(3) + self.webServer = GCDWebServer.init() + guard let webServer = self.webServer else { + fatalError("Unable to create local web server") + } + + webServer.addGETHandler(forBasePath: "/", directoryPath: publicPath!, indexFilename: "index.html", cacheAge: 0, allowRangeRequests: true) + + /* + webServer.addHandler(forMethod: "GET", path: "/", request: GCDWebServerRequest.self, processBlock: { (req) -> GCDWebServerResponse? in + print("Also in here") + return nil + }) + */ + + // Optional config for SPAs to redirect all requests to index file? Not quite right, needs to rewrite instead of redirect + //webServer.addHandler(forMethod: "GET", path: "/", request: GCDWebServerRequest.self, processBlock: { (req) -> GCDWebServerResponse? in + //return GCDWebServerResponse(redirect: URL(string: "index.html")!, permanent: false) + //}) + + do { + let options = [ + GCDWebServerOption_Port: port, + GCDWebServerOption_BindToLocalhost: true, + GCDWebServerOption_ServerName: "Capacitor" + ] as [String : Any] + try webServer.start(options: options) + } catch { + print(error) + } + } + public func configureWebView(configuration: WKWebViewConfiguration) { + configuration.allowsInlineMediaPlayback = true + configuration.suppressesIncrementalRendering = false + configuration.allowsAirPlayForMediaPlayback = true + //configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone + } + + func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { + // Reset the bridge on each navigation + bridge!.reset() + } + + func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { + // TODO: Allow plugins to handle this. See + // https://github.com/ionic-team/cordova-plugin-ionic-webview/blob/608d64191405b233c01a939f5755f8b1fdd97f8c/src/ios/CDVWKWebViewEngine.m#L609 + decisionHandler(.allow) + } + + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + print("⚡️ WebView loaded") + } + + func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { + print("⚡️ WebView failed to load") + print("⚡️ Error: " + error.localizedDescription) + } + + func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { + print("⚡️ WebView failed provisional navigation") + print("⚡️ Error: " + error.localizedDescription) + } + + public override func canPerformUnwindSegueAction(_ action: Selector, from fromViewController: UIViewController, withSender sender: Any) -> Bool { + return false + } + + public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { + let body = message.body + if let dict = body as? [String:Any] { + let type = dict["type"] as? String ?? "" + + if type == "js.error" { + if let error = dict["error"] as! [String:Any]? { + handleJSStartupError(error) + } + } else if type == "message" { + let pluginId = dict["pluginId"] as? String ?? "" + let method = dict["methodName"] as? String ?? "" + let callbackId = dict["callbackId"] as? String ?? "" + + let options = dict["options"] as? [String:Any] ?? [:] + + print("⚡️ To Native -> ", pluginId, method, callbackId, options) + + self.bridge!.handleJSCall(call: JSCall(options: options, pluginId: pluginId, method: method, callbackId: callbackId)) + } else if type == "cordova" { + let pluginId = dict["service"] as? String ?? "" + let method = dict["action"] as? String ?? "" + let callbackId = dict["callbackId"] as? String ?? "" + + let args = dict["actionArgs"] as? Array ?? [] + let options = ["options":args] + + print("To Native Cordova -> ", pluginId, method, callbackId, options) + + self.bridge!.handleCordovaJSCall(call: JSCall(options: options, pluginId: pluginId, method: method, callbackId: callbackId)) + } + } + } + + func handleJSStartupError(_ error: [String:Any]) { + let message = error["message"] ?? "No message" + let url = error["url"] as? String ?? "" + let line = error["line"] ?? "" + let col = error["col"] ?? "" + var filename = "" + if let filenameIndex = url.range(of: "/", options: .backwards)?.lowerBound { + let index = url.index(after: filenameIndex) + filename = String(url[index...]) + } + + print("\n⚡️ ------ STARTUP JS ERROR ------\n") + print("⚡️ \(message)") + print("⚡️ URL: \(url)") + print("⚡️ \(filename):\(line):\(col)") + print("\n⚡️ See above for help with debugging blank-screen issues") + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) { + if bridge != nil { + if motion == .motionShake && bridge!.isDevMode() { + bridge!.showDevMode() + } + } + } + + // We are willing to become first responder to get shake motion + override var canBecomeFirstResponder: Bool { + get { + return true + } + } + + func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) { + + let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert) + + alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in + completionHandler() + })) + + self.present(alertController, animated: true, completion: nil) + } + + func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) { + + let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert) + + alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in + completionHandler(true) + })) + + alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in + completionHandler(false) + })) + + self.present(alertController, animated: true, completion: nil) + } + + func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) { + + let alertController = UIAlertController(title: nil, message: prompt, preferredStyle: .alert) + + alertController.addTextField { (textField) in + textField.text = defaultText + } + + alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in + if let text = alertController.textFields?.first?.text { + completionHandler(text) + } else { + completionHandler(defaultText) + } + + })) + + alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (action) in + + completionHandler(nil) + + })) + + self.present(alertController, animated: true, completion: nil) + } + + public func getWebView() -> WKWebView { + return self.webView! + } + + /** + * Add hooks to detect failed HTTP requests + + func webView(webView: WKWebView, + didFailProvisionalNavigation navigation: WKNavigation!, + withError error: NSError) { + if error.code == -1001 { // TIMED OUT: + // CODE to handle TIMEOUT + } else if error.code == -1003 { // SERVER CANNOT BE FOUND + // CODE to handle SERVER not found + } else if error.code == -1100 { // URL NOT FOUND ON SERVER + // CODE to handle URL not found + } + } + */ + +} + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPBridgedPlugin.h b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPBridgedPlugin.h new file mode 100644 index 0000000..86536fb --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPBridgedPlugin.h @@ -0,0 +1,54 @@ +#import "CAPPluginMethod.h" + +#if defined(__cplusplus) +#define CAP_EXTERN extern "C" __attribute__((visibility("default"))) +#else +#define CAP_EXTERN extern __attribute__((visibility("default"))) +#endif + +#define CAPPluginReturnNone @"none" +#define CAPPluginReturnCallback @"callback" +#define CAPPluginReturnPromise @"promise" +#define CAPPluginReturnWatch @"watch" +#define CAPPluginReturnSync @"sync" // not used + +@class CAPPluginCall; +@class CAPPlugin; + +@protocol CAPBridgedPlugin ++(NSString *)pluginId; ++(NSArray *)pluginMethods; ++(CAPPluginMethod *)getMethod:(NSString *)methodName; +@optional +@end + +#define CAP_PLUGIN_CONFIG(plugin_id) \ +CAP_EXTERN void CapacitorRegisterPlugin(Class); \ ++ (NSString *)pluginId { return @#plugin_id; } \ ++ (void)load { CapacitorRegisterPlugin(self); } +#define CAP_PLUGIN_METHOD(method_name, method_return_type) \ +[methods addObject:[[CAPPluginMethod alloc] initWithName:@#method_name returnType:method_return_type]] + +#define CAP_PLUGIN(objc_name, methods_body) \ +@interface objc_name : NSObject \ +@end \ +@interface objc_name (CAPPluginCategory) \ +@end \ +@implementation objc_name (CAPPluginCategory) \ ++ (NSArray *)pluginMethods { \ + NSMutableArray *methods = [NSMutableArray new]; \ + methods_body \ + return methods; \ +} \ ++ (CAPPluginMethod *)getMethod:(NSString *)methodName { \ + NSArray *methods = [self pluginMethods]; \ + for(CAPPluginMethod *method in methods) { \ + if([method.name isEqualToString:methodName]) { \ + return method; \ + } \ + } \ + return nil; \ +} \ +CAP_PLUGIN_CONFIG(objc_name) \ +@end + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPBridgedPlugin.m b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPBridgedPlugin.m new file mode 100644 index 0000000..5017738 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPBridgedPlugin.m @@ -0,0 +1,23 @@ +#import +#import + +static NSMutableArray *CapacitorPluginClasses; +NSArray *CapacitorGetPluginClasses(void) +{ + return CapacitorPluginClasses; +} + +void CapacitorRegisterPlugin(Class); +void CapacitorRegisterPlugin(Class PluginClass) +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + CapacitorPluginClasses = [NSMutableArray new]; + }); + + // TODO: Make sure the class conforms to the protocol + NSLog(@"Registering Plugin %@", PluginClass); + // Register Plugin + [CapacitorPluginClasses addObject:PluginClass]; +} + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPFile.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPFile.swift new file mode 100644 index 0000000..86ea66d --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPFile.swift @@ -0,0 +1,66 @@ +public class CAPFile { + var url: URL + + public init(url: URL) { + self.url = url + } +} + +/** + * CAPFileManager helps map file schemes to physical files, whether they are on + * disk, in a bundle, or in another location. + */ +@objc public class CAPFileManager: NSObject { + static func get(path: String) -> CAPFile? { + let handlers: [String:CAPFileResolver.Type] = [ + "res://": CAPFileResolverResource.self, + "file://": CAPFileResolverFile.self, + "base64:": CAPFileResolverNotImplemented.self + ] + + for (handlerPrefix, handler) in handlers { + if path.hasPrefix(handlerPrefix) { + return handler.resolve(path: path) + } + } + + return nil + } +} + +private protocol CAPFileResolver { + static func resolve(path: String) -> CAPFile? +} + +private class CAPFileResolverFile: CAPFileResolver { + public static func resolve(path: String) -> CAPFile? { + let manager = FileManager.default + let absPath = path.replacingOccurrences(of: "file:///", with: "") + if !manager.fileExists(atPath: absPath) { + return nil + } + return CAPFile(url: URL(fileURLWithPath: absPath)) + } + +} + +private class CAPFileResolverResource: CAPFileResolver { + public static func resolve(path: String) -> CAPFile? { + let manager = FileManager.default + let bundle = Bundle.main + let resourcePath = bundle.resourcePath + + var absPath = path.replacingOccurrences(of: "res://", with: "") + absPath = resourcePath! + "/" + absPath + if !manager.fileExists(atPath: absPath) { + return nil + } + return CAPFile(url: URL(fileURLWithPath: absPath)) + } +} + +private class CAPFileResolverNotImplemented: CAPFileResolver { + public static func resolve(path: String) -> CAPFile? { + return nil + } +} diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPNotifications.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPNotifications.swift new file mode 100644 index 0000000..c570aef --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPNotifications.swift @@ -0,0 +1,16 @@ +/** + * Notificaton types for NSNotificationCenter + */ +@objc public enum CAPNotifications: Int { + case URLOpen + case UniversalLinkOpen + case ContinueActivity + + public func name() -> String { + switch self { + case .URLOpen: return "CAPNotificationsURLOpen" + case .UniversalLinkOpen: return "CAPUniversalLinkOpen" + case .ContinueActivity: return "CAPNotificationsContinueActivity" + } + } +} diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPlugin.h b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPlugin.h new file mode 100644 index 0000000..38dd9cf --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPlugin.h @@ -0,0 +1,34 @@ +#import +#import + +@class CAPBridge; +@class CAPPluginCall; + +@interface CAPPlugin : NSObject + +@property (nonatomic, strong) WKWebView *webView; +@property (nonatomic, strong) NSString *pluginId; +@property (nonatomic, strong) CAPBridge *bridge; +@property (nonatomic, strong) NSMutableDictionary*> *eventListeners; +@property (nonatomic, strong) NSMutableDictionary *retainedEventArguments; + +- (instancetype) initWithBridge:(CAPBridge*) bridge pluginId:(NSString*) pluginId; +- (void)addEventListener:(NSString *) eventName listener:(CAPPluginCall *)listener; +- (void)removeEventListener:(NSString *) eventName listener:(CAPPluginCall *)listener; +- (void)notifyListeners:(NSString *) eventName data:(NSDictionary*)data; +- (void)notifyListeners:(NSString *) eventName data:(NSDictionary*)data retainUntilConsumed:(BOOL)retain; + +- (NSArray*)getListeners:(NSString *)eventName; +- (BOOL)hasListeners:(NSString *)eventName; +- (void)addListener:(CAPPluginCall *)call; +- (void)removeListener:(CAPPluginCall *)call; + +// Called after init if the plugin wants to do +// some loading so the plugin author doesn't +// need to override init() +-(void) load; +-(NSString *)getId; +-(BOOL)getBool:(CAPPluginCall*) call field:(NSString *)field defaultValue:(BOOL)defaultValue; +-(void)setCenteredPopover:(UIViewController *) vc; + +@end diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPlugin.m b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPlugin.m new file mode 100644 index 0000000..0189137 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPlugin.m @@ -0,0 +1,128 @@ +#import "CAPPlugin.h" +#import + +#import + +@implementation CAPPlugin + +-(instancetype) initWithBridge:(CAPBridge *)bridge pluginId:(NSString *)pluginId { + self.bridge = bridge; + self.pluginId = pluginId; + self.eventListeners = [[NSMutableDictionary alloc] init]; + self.retainedEventArguments = [[NSMutableDictionary alloc] init]; + return self; +} + +-(NSString *) getId { + return self.pluginId; +} + +-(BOOL) getBool:(CAPPluginCall *)call field:(NSString *)field defaultValue:(BOOL)defaultValue { + id idVal = [call.options objectForKey:field]; + + if(![idVal isKindOfClass:[NSNumber class]]) { + return defaultValue; + } + + NSNumber *value = (NSNumber *)idVal; + if(value == nil) { + return defaultValue; + } + if(value.integerValue == 0) { + return FALSE; + } + return TRUE; +} + +-(void)load {} + +- (void)addEventListener:(NSString *)eventName listener:(CAPPluginCall *)listener { + NSMutableArray *listenersForEvent = [self.eventListeners objectForKey:eventName]; + if(!listenersForEvent) { + listenersForEvent = [[NSMutableArray alloc] initWithObjects:listener, nil]; + [self.eventListeners setValue:listenersForEvent forKey:eventName]; + + [self sendRetainedArgumentsForEvent:eventName]; + } else { + [listenersForEvent addObject:listener]; + } +} + +- (void)sendRetainedArgumentsForEvent:(NSString *)eventName { + id retained = [self.retainedEventArguments objectForKey:eventName]; + if (retained == nil) { + return; + } + + [self notifyListeners:eventName data:retained]; + [self.retainedEventArguments removeObjectForKey:eventName]; +} + +- (void)removeEventListener:(NSString *)eventName listener:(CAPPluginCall *)listener { + NSMutableArray *listenersForEvent = [self.eventListeners objectForKey:eventName]; + if(!listenersForEvent) { return; } + NSUInteger listenerIndex = [listenersForEvent indexOfObject:listener]; + if(listenerIndex == NSNotFound) { + return; + } + [listenersForEvent removeObjectAtIndex:listenerIndex]; +} + +- (void)notifyListeners:(NSString *)eventName data:(NSDictionary *)data { + [self notifyListeners:eventName data:data retainUntilConsumed:NO]; +} + +- (void)notifyListeners:(NSString *)eventName data:(NSDictionary *)data retainUntilConsumed:(BOOL)retain { + NSArray *listenersForEvent = [self.eventListeners objectForKey:eventName]; + if(listenersForEvent == nil) { + if (retain == YES) { + [self.retainedEventArguments setObject:data forKey:eventName]; + } + return; + } + + for(CAPPluginCall *call in listenersForEvent) { + CAPPluginCallResult *result = [[CAPPluginCallResult alloc] init:data]; + call.successHandler(result, call); + } +} + +- (void)addListener:(CAPPluginCall *)call { + NSString *eventName = [call.options objectForKey:@"eventName"]; + [self addEventListener:eventName listener:call]; + [call setIsSaved:TRUE]; +} + +- (void)removeListener:(CAPPluginCall *)call { + NSString *eventName = [call.options objectForKey:@"eventName"]; + NSString *callbackId = [call.options objectForKey:@"callbackId"]; + CAPPluginCall *storedCall = [self.bridge getSavedCall:callbackId]; + [self removeEventListener:eventName listener:storedCall]; + [self.bridge releaseCallWithCallbackId:callbackId]; +} + +- (NSArray*)getListeners:(NSString *)eventName { + NSArray* listeners = [self.eventListeners objectForKey:eventName]; + return listeners; +} + +- (BOOL)hasListeners:(NSString *)eventName { + NSArray* listeners = [self.eventListeners objectForKey:eventName]; + + if (listeners == nil) { + return false; + } + return [listeners count] > 0; +} + +/** + * Configure popover sourceRect, sourceView and permittedArrowDirections to show it centered + */ +-(void)setCenteredPopover:(UIViewController *) vc { + vc.popoverPresentationController.sourceRect = CGRectMake(self.bridge.viewController.view.center.x, self.bridge.viewController.view.center.y, 0, 0); + vc.popoverPresentationController.sourceView = self.bridge.viewController.view; + vc.popoverPresentationController.permittedArrowDirections = 0; +} + +@end + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginCall.h b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginCall.h new file mode 100644 index 0000000..6979915 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginCall.h @@ -0,0 +1,38 @@ + +@interface CAPPluginCallResult : NSObject + +@property (nonatomic, strong) NSDictionary* data; + +- (instancetype)init:(NSDictionary*)data; + +@end + +@interface CAPPluginCallError : NSObject + +@property (nonatomic, strong) NSString *message; +@property (nonatomic, strong) NSError *error; +@property (nonatomic, strong) NSDictionary *data; + +- (instancetype)initWithMessage:(NSString *)message error:(NSError *)error data:(NSDictionary*)data; + +@end + +@class CAPPluginCall; + +typedef void(^CAPPluginCallSuccessHandler)(CAPPluginCallResult *result, CAPPluginCall* call); +typedef void(^CAPPluginCallErrorHandler)(CAPPluginCallError *error); + +@interface CAPPluginCall : NSObject + +@property (nonatomic, assign) BOOL isSaved; +@property (nonatomic, strong) NSString *callbackId; +@property (nonatomic, strong) NSDictionary *options; +@property (nonatomic, copy) CAPPluginCallSuccessHandler successHandler; +@property (nonatomic, copy) CAPPluginCallErrorHandler errorHandler; + +- (instancetype)initWithCallbackId:(NSString *)callbackId options:(NSDictionary *)options success:(CAPPluginCallSuccessHandler)success error:(CAPPluginCallErrorHandler)error; + +- (void)save; +@end + + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginCall.m b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginCall.m new file mode 100644 index 0000000..c750434 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginCall.m @@ -0,0 +1,37 @@ +#import + +#import "CAPPluginCall.h" + +@implementation CAPPluginCallResult +- (instancetype)init:(NSDictionary*)data { + self.data = data; + return self; +} +@end + +@implementation CAPPluginCallError + +- (instancetype)initWithMessage:(NSString *)message error:(NSError *)error data:(NSDictionary *)data { + self.message = message; + self.error = error; + self.data = data; + return self; +} + +@end + +@implementation CAPPluginCall + +- (instancetype)initWithCallbackId:(NSString *)callbackId options:(NSDictionary *)options success:(CAPPluginCallSuccessHandler) success error:(CAPPluginCallErrorHandler) error { + self.callbackId = callbackId; + self.options = options; + self.successHandler = success; + self.errorHandler = error; + return self; +} + +- (void)save { + self.isSaved = true; +} + +@end diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginCall.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginCall.swift new file mode 100644 index 0000000..2c6777c --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginCall.swift @@ -0,0 +1,57 @@ +public typealias PluginCallErrorData = [String:Any] +public typealias PluginResultData = [String:Any] +public typealias PluginEventListener = CAPPluginCall + +/** + * Swift niceities for CAPPluginCall + */ +public extension CAPPluginCall { + + public func get(_ key: String, _ ofType: T.Type, _ defaultValue: T? = nil) -> T? { + return self.options[key] as? T ?? defaultValue + } + + public func getArray(_ key: String, _ ofType: T.Type, _ defaultValue: [T]? = nil) -> [T]? { + return self.options[key] as? [T] ?? defaultValue + } + + public func getBool(_ key: String, defaultValue: Bool?) -> Bool? { + return self.options[key] as? Bool ?? defaultValue + } + + public func getInt(_ key: String, defaultValue: Int?) -> Int? { + return self.options[key] as? Int ?? defaultValue + } + + public func getString(_ key: String, defaultValue: String? = nil) -> String? { + return self.options[key] as? String ?? defaultValue + } + + public func getDate(_ key: String, defaultValue: Date? = nil) -> Date? { + guard let isoString = self.options[key] as? String else { + return defaultValue + } + let dateFormatter = DateFormatter() + dateFormatter.locale = Locale(identifier: "en_US_POSIX") + dateFormatter.timeZone = TimeZone.autoupdatingCurrent + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" + return dateFormatter.date(from: isoString) + } + + public func getObject(_ key: String, defaultValue: [String:Any]? = nil) -> [String:Any]? { + return self.options[key] as? [String:Any] ?? defaultValue + } + + public func success() { + successHandler(CAPPluginCallResult(), self) + } + + public func success(_ data: PluginResultData = [:]) { + successHandler(CAPPluginCallResult(data), self) + } + + public func error(_ message: String, _ error: Error? = nil, _ data: PluginCallErrorData = [:]) { + errorHandler(CAPPluginCallError(message: message, error: error, data: data)) + } +} + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginMethod.h b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginMethod.h new file mode 100644 index 0000000..a0a950d --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginMethod.h @@ -0,0 +1,36 @@ +#import "CAPPluginCall.h" +#import "CAPPlugin.h" + +typedef enum { + CAPPluginMethodArgumentNotNullable, + CAPPluginMethodArgumentNullable +} CAPPluginMethodArgumentNullability; + +typedef NSString CAPPluginReturnType; + +/** + * Represents a single argument to a plugin method. + */ +@interface CAPPluginMethodArgument : NSObject + +@property (nonatomic, copy) NSString *name; +@property (nonatomic, assign) CAPPluginMethodArgumentNullability nullability; + +- (instancetype)initWithName:(NSString *)name nullability:(CAPPluginMethodArgumentNullability)nullability type:(NSString *)type; + +@end + +/** + * Represents a method that a plugin supports, with the ability + * to compute selectors and invoke the method. + */ +@interface CAPPluginMethod : NSObject + +@property (nonatomic, assign) SEL selector; +@property (nonatomic, strong) NSString *name; // Raw method name +@property (nonatomic, strong) CAPPluginReturnType *returnType; // Return type of method (i.e. callback/promise/sync) + +- (instancetype)initWithName:(NSString *)name returnType:(CAPPluginReturnType *)returnType; + + +@end diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginMethod.m b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginMethod.m new file mode 100644 index 0000000..34a30af --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/CAPPluginMethod.m @@ -0,0 +1,36 @@ +#import +#import "CAPPluginMethod.h" + +typedef void(^CAPCallback)(id _arg, NSInteger index); + +@implementation CAPPluginMethodArgument + +- (instancetype)initWithName:(NSString *)name nullability:(CAPPluginMethodArgumentNullability)nullability type:(NSString *)type { + self.name = name; + self.nullability = nullability; + return self; +} + +@end + +@implementation CAPPluginMethod { + // NSInvocation's retainArguments doesn't work with our arguments + // so we have to retain args manually + NSMutableArray *_manualRetainArgs; + // Retain invocation instance + NSInvocation *_invocation; + NSMutableArray *_methodArgumentCallbacks; + CAPPluginCall *_call; + SEL _selector; +} + +-(instancetype)initWithName:(NSString *)name returnType:(CAPPluginReturnType *)returnType { + self.name = name; + self.selector = NSSelectorFromString([name stringByAppendingString:@":"]); + self.returnType = returnType; + return self; +} + + +@end + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Capacitor.h b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Capacitor.h new file mode 100644 index 0000000..db855b6 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Capacitor.h @@ -0,0 +1,20 @@ +#import + +//! Project version number for bridge. +FOUNDATION_EXPORT double CapacitorVersionNumber; + +//! Project version string for bridge. +FOUNDATION_EXPORT const unsigned char CapacitorVersionString[]; + +extern NSArray *CapacitorGetPluginClasses(void); + +#import "CAPPlugin.h" +#import "CAPPluginCall.h" +#import "CAPBridgedPlugin.h" +#import "CAPPluginMethod.h" +#import "DefaultPlugins.h" + + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/DevMode.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/DevMode.swift new file mode 100644 index 0000000..ff468b2 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/DevMode.swift @@ -0,0 +1,33 @@ +import Foundation + +/** + * DevMode manages a simple alert popup with developer options for reloading the app, + * among other things. + */ +class DevMode { + var bridge: CAPBridge + init(_ bridge: CAPBridge) { + self.bridge = bridge + } + + func show() { + let alert = UIAlertController(title: "Capacitor Dev Menu", message: nil, preferredStyle: UIAlertControllerStyle.actionSheet) + + alert.addAction(UIAlertAction(title: "Reload", style: .destructive, handler: { (action: UIAlertAction) in + print("Reloading") + self.bridge.reload() + })) + + /* + alert.addAction(UIAlertAction(title: "Toggle Dev Logging", style: .default, handler: { (action: UIAlertAction) in + print("Toggline dev logging") + })) + */ + + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action: UIAlertAction) in + alert.dismiss(animated: true, completion: nil) + })) + + UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil) + } +} diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Diagnostics.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Diagnostics.swift new file mode 100644 index 0000000..820957c --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Diagnostics.swift @@ -0,0 +1,48 @@ +import Foundation + +public class Diagnostics { + /** + * Get current memory usage + */ + public func getMemoryUsage() -> UInt64 { + var taskInfo = mach_task_basic_info() + var count = mach_msg_type_number_t(MemoryLayout.size)/4 + let kerr: kern_return_t = withUnsafeMutablePointer(to: &taskInfo) { + $0.withMemoryRebound(to: integer_t.self, capacity: 1) { + task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count) + } + } + + if kerr == KERN_SUCCESS { + return taskInfo.resident_size + } else { + return 0 + } + } + + /** + * Get free disk space + */ + public func getFreeDiskSize() -> Int64? { + let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) + if let dictionary = try? FileManager.default.attributesOfFileSystem(forPath: paths.last!) { + if let freeSize = dictionary[FileAttributeKey.systemFreeSize] as? NSNumber { + return freeSize.int64Value + } + } + return nil + } + + /** + * Get total disk size + */ + public func getTotalDiskSize() -> Int64?{ + let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) + if let dictionary = try? FileManager.default.attributesOfFileSystem(forPath: paths.last!) { + if let freeSize = dictionary[FileAttributeKey.systemSize] as? NSNumber { + return freeSize.int64Value + } + } + return nil + } +} diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/DocLinks.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/DocLinks.swift new file mode 100644 index 0000000..4eebf07 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/DocLinks.swift @@ -0,0 +1,9 @@ +import Foundation + +enum DocLinks: String { + case CAPPluginMethodSelector = "plugins/ios/#defining-methods" + case NSPhotoLibraryAddUsageDescription = "https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW73" + case NSPhotoLibraryUsageDescription = "https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW17" + case NSCameraUsageDescription = "https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW24" +} + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/JS.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/JS.swift new file mode 100644 index 0000000..a9f2234 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/JS.swift @@ -0,0 +1,119 @@ +import Foundation + +enum JSProcessingError: LocalizedError { + case jsonSerializeError(call: JSCall) + var errorDescription: String? { + switch self { + case .jsonSerializeError(call: let call): + return "Unable to JSON serialize plugin data result for plugin \(call.pluginId) and method \(call.method)" + default: + return "" + } + } +} + +typealias JSObject = [String:Any] +typealias JSArray = [JSObject] + +public class JSDate { + static func toString(_ date: Date) -> String { + let formatter = ISO8601DateFormatter() + return formatter.string(from: date) + } +} + +/** + * A call originating from JavaScript land + */ +public class JSCall { + public var options: [String:Any] = [:] + public var pluginId: String = "" + public var method: String = "" + public var callbackId: String = "" + + public init(options: [String:Any], pluginId: String, method: String, callbackId: String) { + self.options = options + self.pluginId = pluginId + self.method = method + self.callbackId = callbackId + } +} + +public typealias JSResultBody = [String:Any] + +/** + * A result of processing a JSCall, contains + * a reference to the original call and the new result. + */ +public class JSResult { + public var call: JSCall + public var result: JSResultBody + + public init(call: JSCall, result: JSResultBody) { + self.call = call + self.result = result + } + + public func toJson() throws -> String { + do { + if JSONSerialization.isValidJSONObject(result) { + let theJSONData = try JSONSerialization.data(withJSONObject: result, options: []) + + return String(data: theJSONData, + encoding: .utf8)! + } else { + print("[Capacitor Plugin Error] - \(call.pluginId) - \(call.method) - Unable to serialize plugin response as JSON." + + "Ensure that all data passed to success callback from module method is JSON serializable!") + throw JSProcessingError.jsonSerializeError(call: call) + } + } catch let error as JSProcessingError { + throw error + } catch { + print("Unable to serialize plugin response as JSON: \(error.localizedDescription)") + } + + return "{}" + } +} + +public class JSResultError { + var call: JSCall + var error: JSResultBody + var message: String + var errorMessage: String + + public init(call: JSCall, message: String, errorMessage: String, error: JSResultBody) { + self.call = call + self.message = message + self.errorMessage = errorMessage + self.error = error + } + + /** + * Return a linkable error that we can use to help users find help for common exceptions, + * much like AngularJS back in the day. + */ + func getLinkableError(_ message: String) -> String? { + guard let data = message.data(using: .utf8)?.base64EncodedString() else { + return nil + } + + return "\(CAPBridge.CAP_SITE)error/ios?m=\(data)" + } + + public func toJson() -> String { + var jsonResponse = "{}" + + error["message"] = self.message + error["errorMessage"] = self.errorMessage + //error["_exlink"] = getLinkableError(self.message) + + if let theJSONData = try? JSONSerialization.data(withJSONObject: error, options: []) { + jsonResponse = String(data: theJSONData, + encoding: .utf8)! + print("ERROR MESSAGE: ", jsonResponse.prefix(512)) + } + + return jsonResponse + } +} diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/JSExport.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/JSExport.swift new file mode 100644 index 0000000..fbb7ece --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/JSExport.swift @@ -0,0 +1,160 @@ +/** + * PluginExport handles defining JS APIs that map to registered + * plugins and are responsible for proxying calls to our bridge. + */ +public class JSExport { + static let CATCHALL_OPTIONS_PARAM = "_options" + static let CALLBACK_PARAM = "_callback" + + public static func exportCapacitorJS(userContentController: WKUserContentController) throws { + guard let jsUrl = Bundle.main.url(forResource: "public/native-bridge", withExtension: "js") else { + print("ERROR: Required native-bridge.js file in Capacitor not found. Bridge will not function!") + throw BridgeError.errorExportingCoreJS + } + + do { + try self.injectFile(fileURL: jsUrl, userContentController: userContentController) + } catch { + print("ERROR: Unable to read required native-bridge.js file from the Capacitor framework. Bridge will not function!") + throw BridgeError.errorExportingCoreJS + } + } + + public static func exportCordovaJS(userContentController: WKUserContentController) throws { + guard let cordovaUrl = Bundle.main.url(forResource: "public/cordova", withExtension: "js") else { + print("ERROR: Required cordova.js file not found. Cordova plugins will not function!") + throw BridgeError.errorExportingCoreJS + } + guard let cordova_pluginsUrl = Bundle.main.url(forResource: "public/cordova_plugins", withExtension: "js") else { + print("ERROR: Required cordova_plugins.js file not found. Cordova plugins will not function!") + throw BridgeError.errorExportingCoreJS + } + do { + try self.injectFile(fileURL: cordovaUrl, userContentController: userContentController) + try self.injectFile(fileURL: cordova_pluginsUrl, userContentController: userContentController) + } catch { + print("ERROR: Unable to read required cordova files. Cordova plugins will not function!") + throw BridgeError.errorExportingCoreJS + } + + } + + /** + * Export the JS required to implement the given plugin. + */ + public static func exportJS(userContentController: WKUserContentController, pluginClassName: String, pluginType: CAPPlugin.Type) { + var lines = [String]() + + lines.append(""" + (function(w) { + w.Capacitor = w.Capacitor || {}; + w.Capacitor.Plugins = w.Capacitor.Plugins || {}; + var a = w.Capacitor; var p = a.Plugins; + var t = p['\(pluginClassName)'] = {}; + t.addListener = function(eventName, callback) { + return w.Capacitor.addListener('\(pluginClassName)', eventName, callback); + } + t.removeListener = function(eventName, callback) { + return w.Capacitor.removeListener('\(pluginClassName)', eventName, callback); + } + """) + let bridgeType = pluginType as! CAPBridgedPlugin.Type + let methods = bridgeType.pluginMethods() as! [CAPPluginMethod] + for method in methods { + lines.append(generateMethod(pluginClassName: pluginClassName, method: method)) + } + + lines.append(""" + })(window); + """) + + let js = lines.joined(separator: "\n") + + let userScript = WKUserScript(source: js, injectionTime: .atDocumentStart, forMainFrameOnly: true) + userContentController.addUserScript(userScript) + } + + private static func generateMethod(pluginClassName: String, method: CAPPluginMethod) -> String { + let methodName = method.name! + let returnType = method.returnType! + var paramList = [String]() + + // add the catch-all + // options argument which takes a full object and converts each + // key/value pair into an option for plugin call. + paramList.append(CATCHALL_OPTIONS_PARAM) + + // Automatically add the _callback param if returning data through a callback + if returnType == CAPPluginReturnCallback { + paramList.append(CALLBACK_PARAM) + } + + // Create a param string of the form "param1, param2, param3" + let paramString = paramList.joined(separator: ", ") + + // Generate the argument object that will be sent on each call + let argObjectString = CATCHALL_OPTIONS_PARAM + + var lines = [String]() + + // Create the function declaration + lines.append("t['\(method.name!)'] = function(\(paramString)) {") + + // Create the call to Capacitor ... + if returnType == CAPPluginReturnNone { + // ...using none + lines.append(""" + return w.Capacitor.nativeCallback('\(pluginClassName)', '\(methodName)', \(argObjectString)); + """) + } else if returnType == CAPPluginReturnPromise { + + // ...using a promise + lines.append(""" + return w.Capacitor.nativePromise('\(pluginClassName)', '\(methodName)', \(argObjectString)); + """) + } else if returnType == CAPPluginReturnCallback { + // ...using a callback + lines.append(""" + return w.Capacitor.nativeCallback('\(pluginClassName)', '\(methodName)', \(argObjectString), \(CALLBACK_PARAM)); + """) + } else { + print("Error: plugin method return type \(returnType) is not supported!") + } + + // Close the function + lines.append("}") + return lines.joined(separator: "\n") + } + + public static func exportCordovaPluginsJS(userContentController: WKUserContentController) throws { + if let pluginsJSFolder = Bundle.main.url(forResource: "public/plugins", withExtension: nil) { + self.injectFilesForFolder(folder: pluginsJSFolder, userContentController: userContentController) + } + } + + static func injectFilesForFolder(folder: URL, userContentController: WKUserContentController) { + let fileManager = FileManager.default + do { + let fileURLs = try fileManager.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil, options: []) + for fileURL in fileURLs { + if fileURL.hasDirectoryPath { + injectFilesForFolder(folder: fileURL, userContentController: userContentController) + } else { + try self.injectFile(fileURL: fileURL, userContentController: userContentController) + } + } + } catch { + print("Error while enumerating files") + } + } + + static func injectFile(fileURL: URL, userContentController: WKUserContentController) throws { + do { + let data = try String(contentsOf: fileURL, encoding: .utf8) + let userScript = WKUserScript(source: data, injectionTime: .atDocumentStart, forMainFrameOnly: true) + userContentController.addUserScript(userScript) + } catch { + print("Unable to inject js file") + } + } +} diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Accessibility.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Accessibility.swift new file mode 100644 index 0000000..502d9dd --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Accessibility.swift @@ -0,0 +1,38 @@ +import Foundation +import SafariServices + +@objc(Accessibility) +public class Accessibility : CAPPlugin { + static let SCREEN_READER_STATE_CHANGE_EVENT = "accessibilityScreenReaderStateChange" + public override func load() { + NotificationCenter.default.addObserver(self, + selector: #selector(self.onScreenReaderStateChanged(notification:)), + name: Notification.Name(UIAccessibilityVoiceOverStatusChanged), + object: nil) + } + + @objc func onScreenReaderStateChanged(notification: NSNotification) { + notifyListeners(Accessibility.SCREEN_READER_STATE_CHANGE_EVENT, data: [ + "value": UIAccessibilityIsVoiceOverRunning() + ]) + } + + @objc func isScreenReaderEnabled(_ call: CAPPluginCall) { + let voEnabled = UIAccessibilityIsVoiceOverRunning() + call.success([ + "value": voEnabled + ]) + } + + @objc func speak(_ call: CAPPluginCall) { + guard let value = call.getString("value") else { + call.error("No value provided") + return + } + + UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, value) + + call.success() + } +} + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/App.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/App.swift new file mode 100644 index 0000000..46d8f36 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/App.swift @@ -0,0 +1,99 @@ +import Foundation +import SafariServices + +@objc(App) +public class App : CAPPlugin { + var lastUrlOpenOptions: [String:Any?]? + + public override func load() { + NotificationCenter.default.addObserver(self, selector: #selector(self.handleUrlOpened(notification:)), name: Notification.Name(CAPNotifications.URLOpen.name()), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.handleUniversalLink(notification:)), name: Notification.Name(CAPNotifications.UniversalLinkOpen.name()), object: nil) + } + + @objc func handleUrlOpened(notification: NSNotification) { + guard let object = notification.object as? [String:Any?] else { + return + } + + notifyListeners("appUrlOpen", data: makeUrlOpenObject(object), retainUntilConsumed: true) + } + + @objc func handleUniversalLink(notification: NSNotification) { + guard let object = notification.object as? [String:Any?] else { + return + } + + notifyListeners("appUrlOpen", data: makeUrlOpenObject(object), retainUntilConsumed: true) + } + + func makeUrlOpenObject(_ object: [String:Any?]) -> JSObject { + guard let url = object["url"] as? NSURL else { + return [:] + } + + let options = object["options"] as? [String:Any?] ?? [:] + return [ + "url": url.absoluteString ?? "", + "iosSourceApplication": options[UIApplicationOpenURLOptionsKey.sourceApplication.rawValue] as? String ?? "", + "iosOpenInPlace": options[UIApplicationOpenURLOptionsKey.openInPlace.rawValue] as? String ?? "" + ] + } + + func firePluginError(_ jsError: JSProcessingError) { + notifyListeners("pluginError", data: [ + "message": jsError.localizedDescription + ]) + } + + public func fireChange(isActive: Bool) { + notifyListeners("appStateChange", data: [ + "isActive": isActive + ]) + } + + @objc func getLaunchUrl(_ call: CAPPluginCall) { + call.success() + } + + @objc func canOpenUrl(_ call: CAPPluginCall) { + guard let urlString = call.getString("url") else { + call.error("Must supply a URL") + return + } + + guard let url = URL.init(string: urlString) else { + call.error("Invalid URL") + return + } + + DispatchQueue.main.async { + let canOpen = UIApplication.shared.canOpenURL(url) + + call.success([ + "value": canOpen + ]) + } + } + + @objc func openUrl(_ call: CAPPluginCall) { + guard let urlString = call.getString("url") else { + call.error("Must supply a URL") + return + } + + guard let url = URL.init(string: urlString) else { + call.error("Invalid URL") + return + } + + DispatchQueue.main.async { + UIApplication.shared.open(url, options: [:]) { (completed) in + call.success([ + "completed": completed + ]) + } + } + } +} + + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Browser.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Browser.swift new file mode 100644 index 0000000..ddd7cfa --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Browser.swift @@ -0,0 +1,57 @@ +import Foundation +import SafariServices + +@objc(Browser) +public class Browser : CAPPlugin, SFSafariViewControllerDelegate { + var vc: SFSafariViewController? + + @objc func open(_ call: CAPPluginCall) { + guard let urlString = call.getString("url") else { + call.error("Must provide a URL to open") + return + } + + let toolbarColor = call.getString("toolbarColor") + let url = URL(string: urlString) + + DispatchQueue.main.async { + self.vc = SFSafariViewController.init(url: url!) + self.vc!.delegate = self + self.vc!.modalPresentationStyle = .popover + + if toolbarColor != nil { + self.vc!.preferredBarTintColor = UIColor(fromHex: toolbarColor!) + } + + self.setCenteredPopover(self.vc) + self.bridge.viewController.present(self.vc!, animated: true, completion: { + call.success() + }) + } + } + + @objc func close(_ call: CAPPluginCall) { + if vc == nil { + call.success() + } + DispatchQueue.main.async { + self.bridge.viewController.dismiss(animated: true) { + call.success() + } + } + } + + @objc func prefetch(_ call: CAPPluginCall) { + // no-op + call.success() + } + + public func safariViewControllerDidFinish(_ controller: SFSafariViewController) { + self.notifyListeners("browserFinished", data: [:]) + } + + public func safariViewController(_ controller: SFSafariViewController, didCompleteInitialLoad didLoadSuccessfully: Bool) { + self.notifyListeners("browserPageLoaded", data: [:]) + } +} + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Camera.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Camera.swift new file mode 100644 index 0000000..acd0a22 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Camera.swift @@ -0,0 +1,126 @@ +import Foundation +import Photos + +@objc(Camera) +public class Camera : CAPPlugin, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIPopoverPresentationControllerDelegate { + var imagePicker: UIImagePickerController? + var call: CAPPluginCall? + var quality: Float = 1.0 + + @objc func getPhoto(_ call: CAPPluginCall) { + self.call = call + self.quality = call.get("quality", Float.self, 100)! + let allowEditing = call.get("allowEditing", Bool.self, false)! + + // Make sure they have all the necessary info.plist settings + if let missingUsageDescription = checkUsageDescriptions() { + bridge.modulePrint(self, missingUsageDescription) + call.error(missingUsageDescription) + bridge.alert("Camera Error", "Missing required usage description. See console for more information") + return + } + + imagePicker = UIImagePickerController() + imagePicker!.delegate = self + imagePicker!.modalPresentationStyle = .popover + imagePicker!.popoverPresentationController?.delegate = self + self.setCenteredPopover(self.imagePicker!) + //imagePicker!.popoverPresentationController?.sourceView = view + + // Build the action sheet + let alert = UIAlertController(title: "Photo", message: nil, preferredStyle: UIAlertControllerStyle.actionSheet) + alert.addAction(UIAlertAction(title: "From Photos", style: .default, handler: { (action: UIAlertAction) in + let photoAuthorizationStatus = PHPhotoLibrary.authorizationStatus() + if photoAuthorizationStatus == .restricted || photoAuthorizationStatus == .denied { + call.error("User denied access to photos") + return + } + + self.imagePicker!.sourceType = .photoLibrary + self.imagePicker!.allowsEditing = allowEditing + + self.bridge.viewController.present(self.imagePicker!, animated: true, completion: nil) + })) + + alert.addAction(UIAlertAction(title: "Take Picture", style: .default, handler: { (action: UIAlertAction) in + if self.bridge.isSimulator() || !UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.camera) { + self.bridge.modulePrint(self, "Camera not available in simulator") + self.bridge.alert("Camera Error", "Camera not available in Simulator") + call.error("Camera not available while running in Simulator") + return + } + + self.imagePicker!.sourceType = .camera + + self.bridge.viewController.present(self.imagePicker!, animated: true, completion: nil) + })) + + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action: UIAlertAction) in + alert.dismiss(animated: true, completion: nil) + })) + + self.setCenteredPopover(alert) + self.bridge.viewController.present(alert, animated: true, completion: nil) + } + + public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { + picker.dismiss(animated: true) + } + + public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { + var image: UIImage? + + if let editedImage = info[UIImagePickerControllerEditedImage] as? UIImage { + // Use editedImage Here + image = editedImage + } else if let originalImage = info[UIImagePickerControllerOriginalImage] as? UIImage { + // Use originalImage Here + image = originalImage + } + + guard let jpeg = UIImageJPEGRepresentation(image!, CGFloat(quality/100)) else { + print("Unable to convert image to jpeg") + self.call?.error("Unable to convert image to jpeg") + return + } + + let base64String = jpeg.base64EncodedString() + + self.call?.success([ + "base64_data": base64String, + "format": "jpeg" + ]) + + picker.dismiss(animated: true, completion: nil) + } + + /** + * Make sure the developer provided proper usage descriptions + * per apple's terms. + */ + func checkUsageDescriptions() -> String? { + if let dict = Bundle.main.infoDictionary { + let hasPhotoLibraryAddUsage = dict["NSPhotoLibraryAddUsageDescription"] != nil + if !hasPhotoLibraryAddUsage { + let docLink = DocLinks.NSPhotoLibraryAddUsageDescription + return "You are missing NSPhotoLibraryAddUsageDescription in your Info.plist file." + + " Camera will not function without it. Learn more: \(docLink.rawValue)" + } + let hasPhotoLibraryUsage = dict["NSPhotoLibraryUsageDescription"] != nil + if !hasPhotoLibraryUsage { + let docLink = DocLinks.NSPhotoLibraryUsageDescription + return "You are missing NSPhotoLibraryUsageDescription in your Info.plist file." + + " Camera will not function without it. Learn more: \(docLink.rawValue)" + } + let hasCameraUsage = dict["NSCameraUsageDescription"] != nil + if !hasCameraUsage { + let docLink = DocLinks.NSCameraUsageDescription + return "You are missing NSCameraUsageDescription in your Info.plist file." + + " Camera will not function without it. Learn more: \(docLink.rawValue)" + } + } + + return nil + } + +} diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Clipboard.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Clipboard.swift new file mode 100644 index 0000000..e0865eb --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Clipboard.swift @@ -0,0 +1,72 @@ +import Foundation +import CoreMotion + +@objc(Clipboard) +public class Clipboard : CAPPlugin { + @objc func set(_ call: CAPPluginCall) { + guard let options = call.getObject("options") else { + call.error("No options provided") + return + } + + if let string = options["string"] as? String { + UIPasteboard.general.string = string + return + } + if let urlString = options["url"] as? String { + if let url = URL(string: urlString) { + UIPasteboard.general.url = url + } + } + if let imageBase64 = options["image"] as? String { + print(imageBase64) + if let data = Data(base64Encoded: imageBase64) { + let image = UIImage(data: data) + print("Loaded image", image!.size.width, image!.size.height) + UIPasteboard.general.image = image + } else { + print("Unable to encode image") + } + } + + call.success() + } + + @objc func get(_ call: CAPPluginCall) { + guard let options = call.getObject("options") else { + call.error("No options provided") + return + } + + let type = options["type"] as? String ?? "string" + + if type == "string" && UIPasteboard.general.hasStrings { + call.success([ + "value": UIPasteboard.general.string! + ]) + return + } + + if type == "url" && UIPasteboard.general.hasURLs { + let url = UIPasteboard.general.url! + call.success([ + "value": url.absoluteString + ]) + return + } + + if type == "image" && UIPasteboard.general.hasImages { + let image = UIPasteboard.general.image! + let data = UIImagePNGRepresentation(image) + if let base64 = data?.base64EncodedString() { + call.success([ + "value": base64 + ]) + } + return + } + } +} + + + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Console.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Console.swift new file mode 100644 index 0000000..fb82cf2 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Console.swift @@ -0,0 +1,12 @@ +import Foundation + +@objc(Console) +public class Console : CAPPlugin { + + @objc public func log(_ call: CAPPluginCall) { + let message = call.getString("message") ?? "" + let level = call.getString("level") ?? "LOG" + print("⚡️ [\(level)] - \(message)") + } +} + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/DefaultPlugins.h b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/DefaultPlugins.h new file mode 100644 index 0000000..c0acd7b --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/DefaultPlugins.h @@ -0,0 +1,5 @@ +#ifndef DefaultModules_h +#define DefaultModules_h + + +#endif /* DefaultModules_h */ diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/DefaultPlugins.m b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/DefaultPlugins.m new file mode 100644 index 0000000..89c2346 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/DefaultPlugins.m @@ -0,0 +1,107 @@ +#import + +#import "CAPBridgedPlugin.h" + +CAP_PLUGIN(Accessibility, + CAP_PLUGIN_METHOD(isScreenReaderEnabled, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(speak, CAPPluginReturnPromise); +) + +CAP_PLUGIN(App, + CAP_PLUGIN_METHOD(getLaunchUrl, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(canOpenUrl, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(openUrl, CAPPluginReturnPromise); +) + +CAP_PLUGIN(Browser, + CAP_PLUGIN_METHOD(open, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(close, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(prefetch, CAPPluginReturnPromise); +) + +CAP_PLUGIN(Camera, + CAP_PLUGIN_METHOD(getPhoto, CAPPluginReturnPromise); +) + +CAP_PLUGIN(Clipboard, + CAP_PLUGIN_METHOD(get, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(set, CAPPluginReturnPromise); +) + +CAP_PLUGIN(Console, + CAP_PLUGIN_METHOD(log, CAPPluginReturnNone); +) + +CAP_PLUGIN(Device, + CAP_PLUGIN_METHOD(getInfo, CAPPluginReturnPromise); +) + +CAP_PLUGIN(Filesystem, + CAP_PLUGIN_METHOD(readFile, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(writeFile, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(appendFile, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(deleteFile, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(mkdir, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(rmdir, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(readdir, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(stat, CAPPluginReturnPromise); +) + +CAP_PLUGIN(Geolocation, + CAP_PLUGIN_METHOD(getCurrentPosition, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(watchPosition, CAPPluginReturnCallback); + CAP_PLUGIN_METHOD(clearWatch, CAPPluginReturnPromise); +) + +CAP_PLUGIN(Haptics, + CAP_PLUGIN_METHOD(impact, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(selectionStart, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(selectionChanged, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(selectionEnd, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(vibrate, CAPPluginReturnPromise); +) + +CAP_PLUGIN(CAPKeyboard, + CAP_PLUGIN_METHOD(show, CAPPluginReturnPromise); +) + +CAP_PLUGIN(LocalNotifications, + CAP_PLUGIN_METHOD(schedule, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(cancel, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(getPending, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(registerActionTypes, CAPPluginReturnPromise); +) + +CAP_PLUGIN(Modals, + CAP_PLUGIN_METHOD(alert, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(prompt, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(confirm, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(showActions, CAPPluginReturnPromise); +) + +CAP_PLUGIN(Network, + CAP_PLUGIN_METHOD(getStatus, CAPPluginReturnPromise); +) + +CAP_PLUGIN(Photos, + CAP_PLUGIN_METHOD(getPhotos, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(getAlbums, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(createAlbum, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(savePhoto, CAPPluginReturnPromise); +) + +CAP_PLUGIN(Share, + CAP_PLUGIN_METHOD(share, CAPPluginReturnPromise); +) + +CAP_PLUGIN(SplashScreen, + CAP_PLUGIN_METHOD(show, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(hide, CAPPluginReturnPromise); +) + +CAP_PLUGIN(StatusBar, + CAP_PLUGIN_METHOD(setStyle, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(show, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(hide, CAPPluginReturnPromise); +) + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Device.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Device.swift new file mode 100644 index 0000000..684d095 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Device.swift @@ -0,0 +1,48 @@ +import Foundation + +public typealias DeviceInfo = [String:Any] + +@objc(Device) +public class Device: CAPPlugin { + let diagnostics: Diagnostics = Diagnostics() + + @objc func getInfo(_ call: CAPPluginCall) { + var isSimulator = false + #if arch(i386) || arch(x86_64) + isSimulator = true + #endif + + UIDevice.current.isBatteryMonitoringEnabled = true + + let memUsed = diagnostics.getMemoryUsage() + let diskFree = diagnostics.getFreeDiskSize() ?? 0 + let diskTotal = diagnostics.getTotalDiskSize() ?? 0 + + call.success([ + "memUsed": memUsed, + "diskFree": diskFree, + "diskTotal": diskTotal, + "model": UIDevice.current.model, + "osVersion": UIDevice.current.systemVersion, + "appVersion": Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "", + "platform": "ios", + "manufacturer": "Apple", + "uuid": UIDevice.current.identifierForVendor!.uuidString, + "batteryLevel": UIDevice.current.batteryLevel, + "isCharging": UIDevice.current.batteryState == .charging || UIDevice.current.batteryState == .full, + "isVirtual": isSimulator + ]) + + UIDevice.current.isBatteryMonitoringEnabled = false + } + + + @objc func getAdvertisingIdentifier(_ call: CAPPluginCall) { + + } + + @objc func getMemoryUsage(_ call: CAPPluginCall) { + } + +} + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Filesystem.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Filesystem.swift new file mode 100644 index 0000000..749db0f --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Filesystem.swift @@ -0,0 +1,295 @@ +import Foundation + + +@objc(Filesystem) +public class Filesystem : CAPPlugin { + let DEFAULT_DIRECTORY = "DOCUMENTS" + + // Get the SearchPathDirectory corresponding to the JS string + func getDirectory(directory: String) -> FileManager.SearchPathDirectory { + switch directory { + case "DOCUMENTS": + return .documentDirectory + case "APPLICATION": + return .applicationDirectory + case "CACHE": + return .cachesDirectory + default: + return .documentDirectory + } + } + + + /** + * Read a file from the filesystem. + */ + @objc func readFile(_ call: CAPPluginCall) { + //let encoding = call.get("encoding") as? String ?? "utf8" + // TODO: Allow them to switch encoding + guard let file = call.get("path", String.self) else { + handleError(call, "path must be provided and must be a string.") + return + } + + let directoryOption = call.get("directory", String.self, DEFAULT_DIRECTORY)! + let directory = getDirectory(directory: directoryOption) + + guard let dir = FileManager.default.urls(for: directory, in: .userDomainMask).first else { + handleError(call, "Invalid device directory '\(directoryOption)'") + return + } + + let fileUrl = dir.appendingPathComponent(file) + do { + let data = try String(contentsOf: fileUrl, encoding: .utf8) + call.success([ + "data": data + ]) + } catch let error as NSError { + handleError(call, error.localizedDescription, error) + } + } + + + /** + * Write a file to the filesystem. + */ + @objc func writeFile(_ call: CAPPluginCall) { + //let encoding = call.get("encoding") as? String ?? "utf8" + // TODO: Allow them to switch encoding + guard let file = call.get("path", String.self) else { + handleError(call, "path must be provided and must be a string.") + return + } + + guard let data = call.get("data", String.self) else { + handleError(call, "Data must be provided and must be a string.") + return + } + + let directoryOption = call.get("directory", String.self) ?? DEFAULT_DIRECTORY + let directory = getDirectory(directory: directoryOption) + + guard let dir = FileManager.default.urls(for: directory, in: .userDomainMask).first else { + handleError(call, "Invalid device directory '\(directoryOption)'") + return + } + + let fileUrl = dir.appendingPathComponent(file) + do { + try data.write(to: fileUrl, atomically: false, encoding: .utf8) + call.success() + } catch let error as NSError { + handleError(call, error.localizedDescription, error) + } + } + + + /** + * Append to a file. + */ + @objc func appendFile(_ call: CAPPluginCall) { + //let encoding = call.get("encoding") as? String ?? "utf8" + // TODO: Allow them to switch encoding + guard let file = call.get("path", String.self) else { + handleError(call, "path must be provided and must be a string.") + return + } + + guard let data = call.get("data", String.self) else { + handleError(call, "Data must be provided and must be a string.") + return + } + + let directoryOption = call.get("directory", String.self) ?? DEFAULT_DIRECTORY + let directory = getDirectory(directory: directoryOption) + + guard let dir = FileManager.default.urls(for: directory, in: .userDomainMask).first else { + handleError(call, "Invalid device directory '\(directoryOption)'") + return + } + + let fileUrl = dir.appendingPathComponent(file) + + do { + if FileManager.default.fileExists(atPath: fileUrl.path) { + let fileHandle = try FileHandle.init(forWritingTo: fileUrl) + + guard let writeData = data.data(using: .utf8) else { + handleError(call, "Unable to encode data to utf-8") + return + } + + defer { + fileHandle.closeFile() + } + fileHandle.seekToEndOfFile() + fileHandle.write(writeData) + } else { + try data.write(to: fileUrl, atomically: false, encoding: .utf8) + } + call.success() + } catch let error as NSError { + handleError(call, error.localizedDescription, error) + } + } + + /** + * Append to a file. + */ + @objc func deleteFile(_ call: CAPPluginCall) { + //let encoding = call.get("encoding") as? String ?? "utf8" + // TODO: Allow them to switch encoding + guard let file = call.get("path", String.self) else { + handleError(call, "path must be provided and must be a string.") + return + } + + let directoryOption = call.get("directory", String.self) ?? DEFAULT_DIRECTORY + let directory = getDirectory(directory: directoryOption) + + guard let dir = FileManager.default.urls(for: directory, in: .userDomainMask).first else { + handleError(call, "Invalid device directory '\(directoryOption)'") + return + } + + let fileUrl = dir.appendingPathComponent(file) + + do { + + if FileManager.default.fileExists(atPath: fileUrl.path) { + try FileManager.default.removeItem(atPath: fileUrl.path) + } + call.success() + } catch let error as NSError { + handleError(call, error.localizedDescription, error) + } + } + + /** + * Make a new directory, optionally creating parent folders first. + */ + @objc func mkdir(_ call: CAPPluginCall) { + guard let path = call.get("path", String.self) else { + handleError(call, "path must be provided and must be a string.") + return + } + + let createIntermediateDirectories = call.get("createIntermediateDirectories", Bool.self, false)! + let directoryOption = call.get("directory", String.self, DEFAULT_DIRECTORY)! + let directory = getDirectory(directory: directoryOption) + + guard let dir = FileManager.default.urls(for: directory, in: .userDomainMask).first else { + handleError(call, "Invalid device directory '\(directoryOption)'") + return + } + + let fileUrl = dir.appendingPathComponent(path) + + do { + try FileManager.default.createDirectory(at: fileUrl, withIntermediateDirectories: createIntermediateDirectories, attributes: nil) + call.success() + } catch let error as NSError { + handleError(call, error.localizedDescription, error) + } + } + + + /** + * Remove a directory. + */ + @objc func rmdir(_ call: CAPPluginCall) { + guard let path = call.get("path", String.self) else { + handleError(call, "path must be provided and must be a string.") + return + } + + let directoryOption = call.get("directory", String.self, DEFAULT_DIRECTORY)! + let directory = getDirectory(directory: directoryOption) + + guard let dir = FileManager.default.urls(for: directory, in: .userDomainMask).first else { + handleError(call, "Invalid device directory '\(directoryOption)'") + return + } + + let fileUrl = dir.appendingPathComponent(path) + + do { + try FileManager.default.removeItem(at: fileUrl) + call.success() + } catch let error as NSError { + handleError(call, error.localizedDescription, error) + } + } + + + /** + * Read the contents of a directory. + */ + @objc func readdir(_ call: CAPPluginCall) { + guard let path = call.get("path", String.self) else { + handleError(call, "path must be provided and must be a string.") + return + } + + let directoryOption = call.get("directory", String.self, DEFAULT_DIRECTORY)! + let directory = getDirectory(directory: directoryOption) + + guard let dir = FileManager.default.urls(for: directory, in: .userDomainMask).first else { + handleError(call, "Invalid device directory '\(directoryOption)'") + return + } + + let dirUrl = dir.appendingPathComponent(path) + + do { + + let directoryContents = try FileManager.default.contentsOfDirectory(at: dirUrl, includingPropertiesForKeys: nil, options: []) + + let directoryPathStrings = directoryContents.map {(url: URL) -> String in + return url.path + } + + call.success([ + "files": directoryPathStrings + ]) + } catch { + handleError(call, error.localizedDescription, error) + } + } + + @objc func stat(_ call: CAPPluginCall) { + guard let path = call.get("path", String.self) else { + handleError(call, "path must be provided and must be a string.") + return + } + + let directoryOption = call.get("directory", String.self, DEFAULT_DIRECTORY)! + let directory = getDirectory(directory: directoryOption) + + guard let dir = FileManager.default.urls(for: directory, in: .userDomainMask).first else { + handleError(call, "Invalid device directory '\(directoryOption)'") + return + } + + let pathUrl = dir.appendingPathComponent(path) + + do { + let attr = try FileManager.default.attributesOfItem(atPath: pathUrl.path) + call.success([ + "type": attr[.type] as! String, + "size": attr[.size] as! UInt64, + "ctime": (attr[.creationDate] as! Date).timeIntervalSince1970, + "mtime": (attr[.modificationDate] as! Date).timeIntervalSince1970 + ]) + } catch { + handleError(call, error.localizedDescription, error) + } + } + + // Helper function for handling errors + func handleError(_ call: CAPPluginCall, _ message: String, _ error: Error? = nil) { + call.error(message, error) + } +} + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Geolocation.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Geolocation.swift new file mode 100644 index 0000000..9764e52 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Geolocation.swift @@ -0,0 +1,110 @@ +import Foundation +import UIKit +import CoreLocation + +public struct GeolocationCoords { + public var latitude: Double + public var longitude: Double + public init(latitude: Double, longitude: Double) { + self.latitude = latitude + self.longitude = longitude + } +} + +class GetLocationHandler: NSObject, CLLocationManagerDelegate { + var locationManager = CLLocationManager() + var call: CAPPluginCall + + init(call: CAPPluginCall, options: [String:Any]) { + self.call = call + + super.init() + + // TODO: Allow user to configure accuracy, request/authorization mode + self.locationManager.delegate = self + self.locationManager.requestWhenInUseAuthorization() + self.locationManager.desiredAccuracy = kCLLocationAccuracyBest + + if let shouldWatch = options["watch"], shouldWatch as? Bool == true { + self.locationManager.startUpdatingLocation() + } else { + self.locationManager.requestLocation() + } + } + + public func stopUpdating() { + self.locationManager.stopUpdatingLocation() + } + + func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { + call.error(error.localizedDescription, error, [ + "message": error.localizedDescription + ]) + } + + func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + if let location = locations.first { + let result = makePosition(location) + + call.success(result) + } else { + // TODO: Handle case where location is nil + call.success() + } + } + + func makePosition(_ location: CLLocation) -> JSObject { + var ret = JSObject() + var coords = JSObject() + coords["latitude"] = location.coordinate.latitude + coords["longitude"] = location.coordinate.longitude + coords["accuracy"] = location.horizontalAccuracy + coords["altitude"] = location.altitude + coords["speed"] = location.speed + coords["heading"] = location.course + ret["coords"] = coords + return ret + } +} + +@objc(Geolocation) +public class Geolocation : CAPPlugin { + var locationHandler: GetLocationHandler? + var watchLocationHandler: GetLocationHandler? + + @objc func getCurrentPosition(_ call: CAPPluginCall) { + DispatchQueue.main.async { + self.locationHandler = GetLocationHandler(call: call, options:[ + "watch": false + ]) + } + } + + @objc func watchPosition(_ call: CAPPluginCall) { + call.save() + + DispatchQueue.main.async { + self.watchLocationHandler = GetLocationHandler(call: call, options:[ + "watch": true + ]); + } + } + + @objc func clearWatch(_ call: CAPPluginCall) { + guard let callbackId = call.getString("id") else { + print("Must supply id") + return + } + let savedCall = bridge.getSavedCall(callbackId) + if savedCall != nil { + bridge.releaseCall(savedCall!) + + if self.watchLocationHandler != nil { + self.watchLocationHandler?.stopUpdating() + } + } + call.success() + } + +} + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Haptics.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Haptics.swift new file mode 100644 index 0000000..9a91b98 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Haptics.swift @@ -0,0 +1,43 @@ +import Foundation +import AudioToolbox + +@objc(Haptics) +public class Haptics: CAPPlugin { + var selectionFeedbackGenerator: UISelectionFeedbackGenerator? + + @objc public func impact(_ call: CAPPluginCall) { + if let style = call.options["style"] as? String { + var impactStyle = UIImpactFeedbackStyle.heavy + if style == "MEDIUM" { + impactStyle = UIImpactFeedbackStyle.medium + } else if style == "LIGHT" { + impactStyle = UIImpactFeedbackStyle.light + } + let generator = UIImpactFeedbackGenerator(style: impactStyle) + generator.impactOccurred() + } else { + let generator = UIImpactFeedbackGenerator(style: .heavy) + generator.impactOccurred() + } + } + + @objc public func selectionStart(_ call: CAPPluginCall) { + selectionFeedbackGenerator = UISelectionFeedbackGenerator() + selectionFeedbackGenerator?.prepare() + } + + @objc public func selectionChanged(_ call: CAPPluginCall) { + if let generator = selectionFeedbackGenerator { + generator.selectionChanged() + generator.prepare() + } + } + + @objc public func selectionEnd(_ call: CAPPluginCall) { + selectionFeedbackGenerator = nil + } + + @objc public func vibrate(_ call: CAPPluginCall) { + AudioServicesPlayAlertSound(kSystemSoundID_Vibrate) + } +} diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Keyboard.h b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Keyboard.h new file mode 100644 index 0000000..be71513 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Keyboard.h @@ -0,0 +1,19 @@ +#import +#import "CAPPlugin.h" +#import "CAPBridgedPlugin.h" + + +@class CAPPluginCall; + +@interface CAPKeyboard : CAPPlugin + +@property (readwrite, assign, nonatomic) BOOL shrinkView; +@property (readwrite, assign, nonatomic) BOOL disableScrollingInShrinkView; +@property (readwrite, assign, nonatomic) BOOL hideFormAccessoryBar; +@property (readonly, assign, nonatomic) BOOL keyboardIsVisible; + +- (void)setAccessoryBarVisible:(CAPPluginCall*)command; +- (void)hide:(CAPPluginCall*)command; + +@end + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Keyboard.m b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Keyboard.m new file mode 100644 index 0000000..ca561a1 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Keyboard.m @@ -0,0 +1,242 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "Keyboard.h" +#import "CAPBridgedPlugin.h" +#import +#import +#import + +typedef enum : NSUInteger { + ResizeNone, + ResizeNative, + ResizeBody, + ResizeIonic, +} ResizePolicy; + + +@interface CAPKeyboard () + +@property (nonatomic, readwrite, assign) BOOL keyboardIsVisible; +@property (nonatomic, readwrite) ResizePolicy keyboardResizes; +@property (nonatomic, readwrite) int paddingBottom; + +@end + +@implementation CAPKeyboard + +// Define the plugin +//AVOCADO_EXPORT_PLUGIN("com.avocadojs.plugin.keyboard") + + + +- (void)load +{ + self.keyboardResizes = ResizeNative; + BOOL doesResize = YES; + if (!doesResize) { + self.keyboardResizes = ResizeNone; + NSLog(@"CAPIonicKeyboard: no resize"); + + } else { + NSString *resizeMode = @"ionic"; + if (resizeMode) { + if ([resizeMode isEqualToString:@"ionic"]) { + self.keyboardResizes = ResizeIonic; + } else if ([resizeMode isEqualToString:@"body"]) { + self.keyboardResizes = ResizeBody; + } + } + NSLog(@"CAPIonicKeyboard: resize mode %d", self.keyboardResizes); + } + self.hideFormAccessoryBar = YES; + + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + + [nc addObserver:self selector:@selector(onKeyboardDidHide:) name:UIKeyboardDidHideNotification object:nil]; + [nc addObserver:self selector:@selector(onKeyboardDidShow:) name:UIKeyboardDidShowNotification object:nil]; + + [nc removeObserver:self.webView name:UIKeyboardWillChangeFrameNotification object:nil]; + [nc removeObserver:self.webView name:UIKeyboardDidChangeFrameNotification object:nil]; +} + + +#pragma mark Keyboard events + +- (void)resetScrollView +{ + UIScrollView *scrollView = [self.webView scrollView]; + [scrollView setContentInset:UIEdgeInsetsZero]; +} + +- (void)onKeyboardWillHide:(NSNotification *)sender +{ + [self setKeyboardHeight:0 delay:0.01]; + [self resetScrollView]; + [self.bridge evalWithPlugin:self js:@"plugin.fireOnHiding();"]; +} + +- (void)onKeyboardWillShow:(NSNotification *)note +{ + CGRect rect = [[note.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; + double height = rect.size.height; + + double duration = [[note.userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + [self setKeyboardHeight:height delay:duration/2.0]; + [self resetScrollView]; + + NSString *js = [NSString stringWithFormat:@"plugin.fireOnShowing(%d);", (int)height]; + [self.bridge evalWithPlugin:self js:js]; +} + +- (void)onKeyboardDidShow:(NSNotification *)note +{ + CGRect rect = [[note.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; + double height = rect.size.height; + + [self resetScrollView]; + + NSString *js = [NSString stringWithFormat:@"plugin.fireOnShow(%d);", (int)height]; + [self.bridge evalWithPlugin:self js:js]; +} + +- (void)onKeyboardDidHide:(NSNotification *)sender +{ + [self.bridge evalWithPlugin:self js:@"plugin.fireOnHide();"]; + [self resetScrollView]; +} + +- (void)setKeyboardHeight:(int)height delay:(NSTimeInterval)delay +{ + if (self.keyboardResizes != ResizeNone) { + [self setPaddingBottom: height delay:delay]; + } +} + +- (void)setPaddingBottom:(int)paddingBottom delay:(NSTimeInterval)delay +{ + if (self.paddingBottom == paddingBottom) { + return; + } + + self.paddingBottom = paddingBottom; + + __weak CAPKeyboard* weakSelf = self; + SEL action = @selector(_updateFrame); + [NSObject cancelPreviousPerformRequestsWithTarget:weakSelf selector:action object:nil]; + if (delay == 0) { + [self _updateFrame]; + } else { + [weakSelf performSelector:action withObject:nil afterDelay:delay]; + } +} + +- (void)_updateFrame +{ + NSLog(@"CDVIonicKeyboard: updating frame"); + CGRect f = [[UIScreen mainScreen] bounds]; + switch (self.keyboardResizes) { + case ResizeBody: + { + NSString *js = [NSString stringWithFormat:@"plugin.fireOnResize(%d, %d, document.body);", + (int)self.paddingBottom, (int)f.size.height]; + [self.bridge evalWithPlugin:self js:js]; + break; + } + case ResizeIonic: + { + NSString *js = [NSString stringWithFormat:@"plugin.fireOnResize(%d, %d, document.querySelector('ion-app'));", + (int)self.paddingBottom, (int)f.size.height]; + [self.bridge evalWithPlugin:self js:js]; + break; + } + case ResizeNative: + { + [self.webView setFrame:CGRectMake(f.origin.x, f.origin.y, f.size.width, f.size.height - self.paddingBottom)]; + break; + } + default: + break; + } + [self resetScrollView]; +} + + +#pragma mark HideFormAccessoryBar + +static IMP UIOriginalImp; +static IMP WKOriginalImp; + +- (void)setHideFormAccessoryBar:(BOOL)hideFormAccessoryBar +{ + if (hideFormAccessoryBar == _hideFormAccessoryBar) { + return; + } + + NSString* UIClassString = [@[@"UI", @"Web", @"Browser", @"View"] componentsJoinedByString:@""]; + NSString* WKClassString = [@[@"WK", @"Content", @"View"] componentsJoinedByString:@""]; + + Method UIMethod = class_getInstanceMethod(NSClassFromString(UIClassString), @selector(inputAccessoryView)); + Method WKMethod = class_getInstanceMethod(NSClassFromString(WKClassString), @selector(inputAccessoryView)); + + if (hideFormAccessoryBar) { + UIOriginalImp = method_getImplementation(UIMethod); + WKOriginalImp = method_getImplementation(WKMethod); + + IMP newImp = imp_implementationWithBlock(^(id _s) { + return nil; + }); + + method_setImplementation(UIMethod, newImp); + method_setImplementation(WKMethod, newImp); + } else { + method_setImplementation(UIMethod, UIOriginalImp); + method_setImplementation(WKMethod, WKOriginalImp); + } + + _hideFormAccessoryBar = hideFormAccessoryBar; +} + + +#pragma mark Plugin interface + +- (void)setAccessoryBarVisible:(CAPPluginCall *)call +{ + BOOL value = [self getBool:call field:@"visible" defaultValue:FALSE]; + + //NSNumber* value = [call getBool:@"visible" defaultValue:nil]; + NSLog(@"Accessory bar visible change %d", value); + self.hideFormAccessoryBar = !value; + [call successHandler]; +} + +- (void)hide:(CAPPluginCall *)command +{ + [self.webView endEditing:YES]; +} + + +#pragma mark dealloc + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +@end + + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/LocalNotifications.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/LocalNotifications.swift new file mode 100644 index 0000000..ff955a8 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/LocalNotifications.swift @@ -0,0 +1,582 @@ +import Foundation +import UserNotifications + +enum LocalNotificationError: LocalizedError { + case contentNoId + case contentNoTitle + case contentNoBody + case triggerConstructionFailed + case triggerRepeatIntervalTooShort + case attachmentNoId + case attachmentNoUrl + case attachmentFileNotFound(path: String) + case attachmentUnableToCreate(String) + + var errorDescription: String? { + switch self { + case .attachmentFileNotFound(path: let path): + return "Unable to find file \(path) for attachment" + default: + return "" + } + } +} + + +/** + * Implement three common modal types: alert, confirm, and prompt + */ +@objc(LocalNotifications) +public class LocalNotifications : CAPPlugin, UNUserNotificationCenterDelegate { + // Local list of notification id -> JSObject for storing options + // between notification requets + var notificationRequestLookup = [String:JSObject]() + + public override func load() { + let center = UNUserNotificationCenter.current() + center.delegate = self + } + + /** + * Schedule a notification. + */ + @objc func schedule(_ call: CAPPluginCall) { + guard let notifications = call.getArray("notifications", [String:Any].self) else { + call.error("Must provide notifications array as notifications option") + return + } + + requestPermissions() + + var ids = [String]() + + for notification in notifications { + guard let identifier = notification["id"] as? String else { + call.error("Notification missing identifier") + return + } + + let extra = notification["options"] as? JSObject ?? [:] + + var content: UNNotificationContent + do { + content = try makeNotificationContent(notification) + } catch { + print(error.localizedDescription) + call.error("Unable to make notification", error) + return + } + + var trigger: UNNotificationTrigger? + + do { + if let schedule = notification["schedule"] as? [String:Any] { + try trigger = handleScheduledNotification(call, schedule) + } + } catch { + call.error("Unable to create notification, trigger failed", error) + return + } + + // Schedule the request. + let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger) + + notificationRequestLookup[request.identifier] = notification + + let center = UNUserNotificationCenter.current() + center.add(request) { (error : Error?) in + if let theError = error { + print(theError.localizedDescription) + call.error(theError.localizedDescription) + } + } + + ids.append(request.identifier) + } + + call.success([ + "ids": ids + ]) + } + + /** + * Cancel notifications by id + */ + @objc func cancel(_ call: CAPPluginCall) { + guard let notifications = call.getArray("notifications", JSObject.self, []) else { + call.error("Must supply notifications to cancel") + return + } + + let ids = notifications.map { $0["id"] as? String ?? "" } + + UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ids) + } + + /** + * Get all pending notifications. + */ + @objc func getPending(_ call: CAPPluginCall) { + UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: { (notifications) in + print("num of pending notifications \(notifications.count)") + print(notifications) + + let ret = notifications.map({ (notification) -> [String:Any] in + return self.makeNotificationRequestJSObject(notification) + }) + call.success([ + "notifications": ret + ]) + }) + } + + /** + * Register allowed action types that a notification may present. + */ + @objc func registerActionTypes(_ call: CAPPluginCall) { + guard let types = call.getArray("types", Any.self) as? JSArray else { + return + } + + makeActionTypes(types) + + call.success() + } + + /** + * Request permissions to send notifications + */ + func requestPermissions() { + // Override point for customization after application launch. + let center = UNUserNotificationCenter.current() + center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in + // Enable or disable features based on authorization. + } + + DispatchQueue.main.async { + UIApplication.shared.registerForRemoteNotifications() + } + } + + /** + * Handle delegate willPresent action when the app is in the foreground. + * This controls how a notification is presented when the app is running, such as + * whether it should stay silent, display a badge, play a sound, or show an alert. + */ + public func userNotificationCenter(_ center: UNUserNotificationCenter, + willPresent notification: UNNotification, + withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + let request = notification.request + + notifyListeners("localNotificationReceived", data: makeNotificationRequestJSObject(request)) + + if let options = notificationRequestLookup[request.identifier] { + let silent = options["silent"] as? Bool ?? false + if silent { + completionHandler(.init(rawValue:0)) + return + } + } + + if bridge.isAppActive() { + completionHandler([.badge, .sound, .alert]) + } else { + completionHandler([.badge, .sound]) + } + } + + /** + * Handle didReceive action, called when a notification opens or activates + * the app based on an action. + */ + public func userNotificationCenter(_ center: UNUserNotificationCenter, + didReceive response: UNNotificationResponse, + withCompletionHandler completionHandler: @escaping () -> Void) { + completionHandler() + + var data = JSObject() + + // Get the info for the original notification request + let originalNotificationRequest = response.notification.request + + let actionId = response.actionIdentifier + + // We turn the two default actions (open/dismiss) into generic strings + if actionId == UNNotificationDefaultActionIdentifier { + data["actionId"] = "tap" + } else if actionId == UNNotificationDismissActionIdentifier { + data["actionId"] = "dismiss" + } else { + data["actionId"] = actionId + } + + // If the type of action was for an input type, get the value + if let inputType = response as? UNTextInputNotificationResponse { + data["inputValue"] = inputType.userText + } + + data["notificationRequest"] = makeNotificationRequestJSObject(originalNotificationRequest) + + notifyListeners("localNotificationActionPerformed", data: data) + } + + /** + * Build the content for a notification. + */ + func makeNotificationContent(_ notification: JSObject) throws -> UNNotificationContent { + guard let title = notification["title"] as? String else { + throw LocalNotificationError.contentNoTitle + } + guard let body = notification["body"] as? String else { + throw LocalNotificationError.contentNoBody + } + + let actionTypeId = notification["actionTypeId"] as? String + let sound = notification["sound"] as? String + let attachments = notification["attachments"] as? JSArray + + let content = UNMutableNotificationContent() + content.title = NSString.localizedUserNotificationString(forKey: title, arguments: nil) + content.body = NSString.localizedUserNotificationString(forKey: body, + arguments: nil) + + if actionTypeId != nil { + content.categoryIdentifier = actionTypeId! + } + + if sound != nil { + content.sound = UNNotificationSound(named: sound!) + } + + if attachments != nil { + content.attachments = try makeAttachments(attachments!) + } + + return content + } + + /** + * Build a notification trigger, such as triggering each N seconds, or + * on a certain date "shape" (such as every first of the month) + */ + func handleScheduledNotification(_ call: CAPPluginCall, _ schedule: [String:Any]) throws -> UNNotificationTrigger? { + let at = schedule["at"] as? Date + let every = schedule["every"] as? String + let on = schedule["on"] as? [String:Int] + let repeats = schedule["repeats"] as? Bool ?? false + + // If there's a specific date for this notificiation + if at != nil { + let dateInfo = Calendar.current.dateComponents(in: TimeZone.current, from: at!) + + if dateInfo.date! < Date() { + call.error("Scheduled time must be *after* current time") + return nil + } + + var dateInterval = DateInterval(start: Date(), end: dateInfo.date!) + + // Notifications that repeat have to be at least a minute between each other + if repeats && dateInterval.duration < 60 { + throw LocalNotificationError.triggerRepeatIntervalTooShort + } + + return UNTimeIntervalNotificationTrigger(timeInterval: dateInterval.duration, repeats: repeats) + } + + // If this notification should repeat every day/month/week/etc. or on a certain + // matching set of date components + if on != nil { + let dateComponents = getDateComponents(on!) + return UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true) + } + + if every != nil { + if let repeatDateInterval = getRepeatDateInterval(every!) { + return UNTimeIntervalNotificationTrigger(timeInterval: repeatDateInterval.duration, repeats: true) + } + } + + return nil + } + + /** + * Given our schedule format, return a DateComponents object + * that only contains the components passed in. + */ + func getDateComponents(_ at: [String:Int]) -> DateComponents { + //var dateInfo = Calendar.current.dateComponents(in: TimeZone.current, from: Date()) + //dateInfo.calendar = Calendar.current + var dateInfo = DateComponents() + + if let year = at["year"] { + dateInfo.year = year + } + if let month = at["month"] { + dateInfo.month = month + } + if let day = at["day"] { + dateInfo.day = day + } + if let hour = at["hour"] { + dateInfo.hour = hour + } + if let minute = at["minute"] { + dateInfo.minute = minute + } + if let second = at["second"] { + dateInfo.second = second + } + return dateInfo + } + + /** + * Compute the difference between the string representation of a date + * interval and today. For example, if every is "month", then we + * return the interval between today and a month from today. + */ + func getRepeatDateInterval(_ every: String) -> DateInterval? { + let cal = Calendar.current + let now = Date() + switch every { + case "year": + let newDate = cal.date(byAdding: .year, value: 1, to: now)! + return DateInterval(start: now, end: newDate) + case "month": + let newDate = cal.date(byAdding: .month, value: 1, to: now)! + return DateInterval(start: now, end: newDate) + case "two-weeks": + let newDate = cal.date(byAdding: .weekOfYear, value: 2, to: now)! + return DateInterval(start: now, end: newDate) + case "week": + let newDate = cal.date(byAdding: .weekOfYear, value: 1, to: now)! + return DateInterval(start: now, end: newDate) + case "day": + let newDate = cal.date(byAdding: .day, value: 1, to: now)! + return DateInterval(start: now, end: newDate) + case "day": + let newDate = cal.date(byAdding: .day, value: 1, to: now)! + return DateInterval(start: now, end: newDate) + case "hour": + let newDate = cal.date(byAdding: .hour, value: 1, to: now)! + return DateInterval(start: now, end: newDate) + case "minute": + let newDate = cal.date(byAdding: .minute, value: 1, to: now)! + return DateInterval(start: now, end: newDate) + case "second": + let newDate = cal.date(byAdding: .second, value: 1, to: now)! + return DateInterval(start: now, end: newDate) + default: + return nil + } + } + + /** + * Turn a UNNotificationRequest into a JSObject to return back to the client. + */ + func makeNotificationRequestJSObject(_ request: UNNotificationRequest) -> JSObject { + let notificationRequest = notificationRequestLookup[request.identifier] ?? [:] + + return [ + "id": request.identifier, + "extra": notificationRequest["extra"] ?? [:] + ] + } + + /** + * Make required UNNotificationCategory entries for action types + */ + func makeActionTypes(_ actionTypes: JSArray) { + var createdCategories = [UNNotificationCategory]() + + let generalCategory = UNNotificationCategory(identifier: "GENERAL", + actions: [], + intentIdentifiers: [], + options: .customDismissAction) + + createdCategories.append(generalCategory) + for type in actionTypes { + guard let id = type["id"] as? String else { + bridge.modulePrint(self, "Action type must have an id field") + continue + } + let hiddenBodyPlaceholder = type["iosHiddenPreviewsBodyPlaceholder"] as? String ?? "" + let actions = type["actions"] as? JSArray ?? [] + + let newActions = makeActions(actions) + + // Create the custom actions for the TIMER_EXPIRED category. + var newCategory: UNNotificationCategory? + + if #available(iOS 11.0, *) { + newCategory = UNNotificationCategory(identifier: id, + actions: newActions, + intentIdentifiers: [], + hiddenPreviewsBodyPlaceholder: hiddenBodyPlaceholder, + options: makeCategoryOptions(type)) + } else { + newCategory = UNNotificationCategory(identifier: id, + actions: newActions, + intentIdentifiers: [], + options: makeCategoryOptions(type)) + } + + createdCategories.append(newCategory!) + } + + let center = UNUserNotificationCenter.current() + center.setNotificationCategories(Set(createdCategories)) + } + + + /** + * Build the required UNNotificationAction objects for each action type registered. + */ + func makeActions(_ actions: JSArray) -> [UNNotificationAction] { + var createdActions = [UNNotificationAction]() + + for action in actions { + guard let id = action["id"] as? String else { + bridge.modulePrint(self, "Action must have an id field") + continue + } + let title = action["title"] as? String ?? "" + let input = action["input"] as? Bool ?? false + + var newAction: UNNotificationAction + if input { + let inputButtonTitle = action["inputButtonTitle"] as? String + let inputPlaceholder = action["inputPlaceholder"] as? String ?? "" + + if inputButtonTitle != nil { + newAction = UNTextInputNotificationAction(identifier: id, + title: title, + options: makeActionOptions(action), + textInputButtonTitle: inputButtonTitle!, + textInputPlaceholder: inputPlaceholder) + } else { + newAction = UNTextInputNotificationAction(identifier: id, title: title, options: makeActionOptions(action)) + } + } else { + // Create the custom actions for the TIMER_EXPIRED category. + newAction = UNNotificationAction(identifier: id, + title: title, + options: makeActionOptions(action)) + } + createdActions.append(newAction) + } + + return createdActions + } + + /** + * Make options for UNNotificationActions + */ + func makeActionOptions(_ action: [String:Any]) -> UNNotificationActionOptions { + let foreground = action["foreground"] as? Bool ?? false + let destructive = action["destructive"] as? Bool ?? false + let requiresAuthentication = action["requiresAuthentication"] as? Bool ?? false + + if foreground { + return .foreground + } + if destructive { + return .destructive + } + if requiresAuthentication { + return .authenticationRequired + } + return UNNotificationActionOptions(rawValue: 0) + } + + /** + * Make options for UNNotificationCategoryActions + */ + func makeCategoryOptions(_ type: JSObject) -> UNNotificationCategoryOptions { + let customDismiss = type["iosCustomDismissAction"] as? Bool ?? false + let carPlay = type["iosAllowInCarPlay"] as? Bool ?? false + let hiddenPreviewsShowTitle = type["iosHiddenPreviewsShowTitle"] as? Bool ?? false + let hiddenPreviewsShowSubtitle = type["iosHiddenPreviewsShowSubtitle"] as? Bool ?? false + + if customDismiss { + return .customDismissAction + } + if carPlay { + return .allowInCarPlay + } + + // New iOS 11 features + if #available(iOS 11.0, *) { + // Running iOS 11 OR NEWER + if hiddenPreviewsShowTitle { + return .hiddenPreviewsShowTitle + } + if hiddenPreviewsShowSubtitle { + return .hiddenPreviewsShowSubtitle + } + } + + return UNNotificationCategoryOptions(rawValue: 0) + } + + /** + * Build the UNNotificationAttachment object for each attachment supplied. + */ + func makeAttachments(_ attachments: JSArray) throws -> [UNNotificationAttachment] { + var createdAttachments = [UNNotificationAttachment]() + + for a in attachments { + guard let id = a["id"] as? String else { + throw LocalNotificationError.attachmentNoId + } + guard let url = a["url"] as? String else { + throw LocalNotificationError.attachmentNoUrl + } + guard let urlObject = makeAttachmentUrl(url) else { + throw LocalNotificationError.attachmentFileNotFound(path: url) + } + + let options = a["options"] as? JSObject ?? [:] + + do { + let newAttachment = try UNNotificationAttachment(identifier: id, url: urlObject, options: makeAttachmentOptions(options)) + createdAttachments.append(newAttachment) + } catch { + throw LocalNotificationError.attachmentUnableToCreate(error.localizedDescription) + } + } + + return createdAttachments + } + + /** + * Get the internal URL for the attachment URL + */ + func makeAttachmentUrl(_ path: String) -> URL? { + let file = CAPFileManager.get(path: path) + return file?.url + } + + /** + * Build the options for the attachment, if any. (For example: the clipping rectangle to use + * for image attachments) + */ + func makeAttachmentOptions(_ options: JSObject) -> [AnyHashable:Any] { + var opts = [AnyHashable:Any]() + + if let iosUNNotificationAttachmentOptionsTypeHintKey = options["iosUNNotificationAttachmentOptionsTypeHintKey"] as? String { + opts[UNNotificationAttachmentOptionsTypeHintKey] = iosUNNotificationAttachmentOptionsTypeHintKey + } + if let iosUNNotificationAttachmentOptionsThumbnailHiddenKey = options["iosUNNotificationAttachmentOptionsThumbnailHiddenKey"] as? String { + opts[UNNotificationAttachmentOptionsThumbnailHiddenKey] = iosUNNotificationAttachmentOptionsThumbnailHiddenKey + } + if let iosUNNotificationAttachmentOptionsThumbnailClippingRectKey = options["iosUNNotificationAttachmentOptionsThumbnailClippingRectKey"] as? String { + opts[UNNotificationAttachmentOptionsThumbnailClippingRectKey] = iosUNNotificationAttachmentOptionsThumbnailClippingRectKey + } + if let iosUNNotificationAttachmentOptionsThumbnailTimeKey = options["iosUNNotificationAttachmentOptionsThumbnailTimeKey"] as? String { + opts[UNNotificationAttachmentOptionsThumbnailTimeKey] = iosUNNotificationAttachmentOptionsThumbnailTimeKey + } + return opts + } +} + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Modals.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Modals.swift new file mode 100644 index 0000000..64a2a1b --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Modals.swift @@ -0,0 +1,125 @@ +import Foundation + +/** + * Implement three common modal types: alert, confirm, and prompt + */ +@objc(Modals) +public class Modals : CAPPlugin { + @objc public func alert(_ call: CAPPluginCall) { + guard let title = call.options["title"] as? String else { + call.error("title must be provided") + return + } + let message = call.options["message"] as? String + let buttonTitle = call.options["buttonTitle"] as? String ?? "OK" + + let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert) + alert.addAction(UIAlertAction(title: buttonTitle, style: UIAlertActionStyle.default, handler: nil)) + + DispatchQueue.main.async { + self.bridge.viewController.present(alert, animated: true, completion: nil) + } + + // Call success immediately + call.success() + } + + @objc public func confirm(_ call: CAPPluginCall) { + guard let title = call.options["title"] as? String else { + call.error("title must be provided") + return + } + let message = call.options["message"] as? String ?? "" + let okButtonTitle = call.options["okButtonTitle"] as? String ?? "OK" + let cancelButtonTitle = call.options["cancelButtonTitle"] as? String ?? "Cancel" + + let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert) + alert.addAction(UIAlertAction(title: okButtonTitle, style: UIAlertActionStyle.default, handler: { (action) -> Void in + call.success([ + "value": true + ]) + })) + alert.addAction(UIAlertAction(title: cancelButtonTitle, style: UIAlertActionStyle.default, handler: { (action) -> Void in + call.success([ + "value": false + ]) + })) + + DispatchQueue.main.async { + self.bridge.viewController.present(alert, animated: true, completion: nil) + } + } + + @objc public func prompt (_ call: CAPPluginCall) { + guard let title = call.options["title"] as? String else { + call.error("title must be provided") + return + } + let message = call.options["message"] as? String ?? "" + let okButtonTitle = call.options["okButtonTitle"] as? String ?? "OK" + let cancelButtonTitle = call.options["cancelButtonTitle"] as? String ?? "Cancel" + let inputPlaceholder = call.options["inputPlaceholder"] as? String ?? "" + + let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert) + + DispatchQueue.main.async { + + alert.addTextField { (textField) in + textField.text = inputPlaceholder + } + + alert.addAction(UIAlertAction(title: okButtonTitle, style: UIAlertActionStyle.default, handler: { (action) -> Void in + let textField = alert.textFields![0] as UITextField + call.success([ + "value": textField.text ?? "", + "cancelled": false + ]) + })) + alert.addAction(UIAlertAction(title: cancelButtonTitle, style: UIAlertActionStyle.default, handler: { (action) -> Void in + call.success([ + "value": "", + "cancelled": true + ]) + })) + + self.bridge.viewController.present(alert, animated: true, completion: nil) + } + } + + @objc func showActions(_ call: CAPPluginCall) { + guard let title = call.options["title"] as? String else { + call.error("title must be provided") + return + } + let message = call.options["message"] as? String ?? "" + + let options = call.getArray("options", JSObject.self) ?? [] + + DispatchQueue.main.async { + let alertController = self.buildActionSheet(call, title: title, message: message, options: options) + + self.bridge.viewController.present(alertController, animated: true, completion: nil) + } + } + + + func buildActionSheet(_ call: CAPPluginCall, title: String, message: String, options: JSArray) -> UIAlertController { + let controller = UIAlertController(title: title, message: message, preferredStyle: .actionSheet) + + for (index, option) in options.enumerated() { + let style = option["style"] as? String ?? "DEFAULT" + let title = option["title"] as? String ?? "" + + let action = UIAlertAction(title: title, style: style == "DESTRUCTIVE" ? .destructive : .default, handler: { (action) -> Void in + call.success([ + "index": index + ]) + }) + + controller.addAction(action) + } + self.setCenteredPopover(controller) + + return controller + } +} diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Network/Network.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Network/Network.swift new file mode 100644 index 0000000..cac007a --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Network/Network.swift @@ -0,0 +1,54 @@ +import Foundation + +@objc(Network) +public class Network : CAPPlugin { + let reachability = Reachability()! + + public override func load() { + print("Loading network plugin") + reachability.whenReachable = { reachability in + if reachability.connection == .wifi { + print("Reachable via WiFi") + self.notifyStatusChangeListeners(connected: true, type: "wifi") + } else { + print("Reachable via Cellular") + self.notifyStatusChangeListeners(connected: true, type: "cellular") + } + } + reachability.whenUnreachable = { _ in + print("Not reachable") + self.notifyStatusChangeListeners(connected: false, type: "none") + } + + do { + try reachability.startNotifier() + } catch { + print("Unable to start notifier") + } + } + + @objc func getStatus(_ call: CAPPluginCall) { + var connected = false + var type = "" + if reachability.connection == .wifi { + connected = true + type = "wifi" + } + if reachability.connection == .cellular { + connected = true + type = "cellular" + } + + call.success([ + "connected": connected, + "connectionType": type + ]) + } + + func notifyStatusChangeListeners(connected: Bool, type: String) { + notifyListeners("networkStatusChange", data: [ + "connected": connected, + "connectionType": type + ]) + } +} diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Network/Reachability.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Network/Reachability.swift new file mode 100644 index 0000000..304b311 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Network/Reachability.swift @@ -0,0 +1,322 @@ +/* + Copyright (c) 2014, Ashley Mills + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +import SystemConfiguration +import Foundation + +public enum ReachabilityError: Error { + case FailedToCreateWithAddress(sockaddr_in) + case FailedToCreateWithHostname(String) + case UnableToSetCallback + case UnableToSetDispatchQueue +} + +@available(*, unavailable, renamed: "Notification.Name.reachabilityChanged") +public let ReachabilityChangedNotification = NSNotification.Name("ReachabilityChangedNotification") + +extension Notification.Name { + public static let reachabilityChanged = Notification.Name("reachabilityChanged") +} + +func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutableRawPointer?) { + + guard let info = info else { return } + + let reachability = Unmanaged.fromOpaque(info).takeUnretainedValue() + reachability.reachabilityChanged() +} + +public class Reachability { + + public typealias NetworkReachable = (Reachability) -> () + public typealias NetworkUnreachable = (Reachability) -> () + + @available(*, unavailable, renamed: "Conection") + public enum NetworkStatus: CustomStringConvertible { + case notReachable, reachableViaWiFi, reachableViaWWAN + public var description: String { + switch self { + case .reachableViaWWAN: return "Cellular" + case .reachableViaWiFi: return "WiFi" + case .notReachable: return "No Connection" + } + } + } + + public enum Connection: CustomStringConvertible { + case none, wifi, cellular + public var description: String { + switch self { + case .cellular: return "Cellular" + case .wifi: return "WiFi" + case .none: return "No Connection" + } + } + } + + public var whenReachable: NetworkReachable? + public var whenUnreachable: NetworkUnreachable? + + @available(*, deprecated: 4.0, renamed: "allowsCellularConnection") + public let reachableOnWWAN: Bool = true + + /// Set to `false` to force Reachability.connection to .none when on cellular connection (default value `true`) + public var allowsCellularConnection: Bool + + // The notification center on which "reachability changed" events are being posted + public var notificationCenter: NotificationCenter = NotificationCenter.default + + @available(*, deprecated: 4.0, renamed: "connection.description") + public var currentReachabilityString: String { + return "\(connection)" + } + + @available(*, unavailable, renamed: "connection") + public var currentReachabilityStatus: Connection { + return connection + } + + public var connection: Connection { + + guard isReachableFlagSet else { return .none } + + // If we're reachable, but not on an iOS device (i.e. simulator), we must be on WiFi + guard isRunningOnDevice else { return .wifi } + + var connection = Connection.none + + if !isConnectionRequiredFlagSet { + connection = .wifi + } + + if isConnectionOnTrafficOrDemandFlagSet { + if !isInterventionRequiredFlagSet { + connection = .wifi + } + } + + if isOnWWANFlagSet { + if !allowsCellularConnection { + connection = .none + } else { + connection = .cellular + } + } + + return connection + } + + fileprivate var previousFlags: SCNetworkReachabilityFlags? + + fileprivate var isRunningOnDevice: Bool = { + #if (arch(i386) || arch(x86_64)) && os(iOS) + return false + #else + return true + #endif + }() + + fileprivate var notifierRunning = false + fileprivate let reachabilityRef: SCNetworkReachability + + fileprivate let reachabilitySerialQueue = DispatchQueue(label: "uk.co.ashleymills.reachability") + + required public init(reachabilityRef: SCNetworkReachability) { + allowsCellularConnection = true + self.reachabilityRef = reachabilityRef + } + + public convenience init?(hostname: String) { + + guard let ref = SCNetworkReachabilityCreateWithName(nil, hostname) else { return nil } + + self.init(reachabilityRef: ref) + } + + public convenience init?() { + + var zeroAddress = sockaddr() + zeroAddress.sa_len = UInt8(MemoryLayout.size) + zeroAddress.sa_family = sa_family_t(AF_INET) + + guard let ref = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress) else { return nil } + + self.init(reachabilityRef: ref) + } + + deinit { + stopNotifier() + } +} + +public extension Reachability { + + // MARK: - *** Notifier methods *** + func startNotifier() throws { + + guard !notifierRunning else { return } + + var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) + context.info = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) + if !SCNetworkReachabilitySetCallback(reachabilityRef, callback, &context) { + stopNotifier() + throw ReachabilityError.UnableToSetCallback + } + + if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef, reachabilitySerialQueue) { + stopNotifier() + throw ReachabilityError.UnableToSetDispatchQueue + } + + // Perform an initial check + reachabilitySerialQueue.async { + self.reachabilityChanged() + } + + notifierRunning = true + } + + func stopNotifier() { + defer { notifierRunning = false } + + SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil) + SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil) + } + + // MARK: - *** Connection test methods *** + @available(*, deprecated: 4.0, message: "Please use `connection != .none`") + var isReachable: Bool { + + guard isReachableFlagSet else { return false } + + if isConnectionRequiredAndTransientFlagSet { + return false + } + + if isRunningOnDevice { + if isOnWWANFlagSet && !reachableOnWWAN { + // We don't want to connect when on cellular connection + return false + } + } + + return true + } + + @available(*, deprecated: 4.0, message: "Please use `connection == .cellular`") + var isReachableViaWWAN: Bool { + // Check we're not on the simulator, we're REACHABLE and check we're on WWAN + return isRunningOnDevice && isReachableFlagSet && isOnWWANFlagSet + } + + @available(*, deprecated: 4.0, message: "Please use `connection == .wifi`") + var isReachableViaWiFi: Bool { + + // Check we're reachable + guard isReachableFlagSet else { return false } + + // If reachable we're reachable, but not on an iOS device (i.e. simulator), we must be on WiFi + guard isRunningOnDevice else { return true } + + // Check we're NOT on WWAN + return !isOnWWANFlagSet + } + + var description: String { + + let W = isRunningOnDevice ? (isOnWWANFlagSet ? "W" : "-") : "X" + let R = isReachableFlagSet ? "R" : "-" + let c = isConnectionRequiredFlagSet ? "c" : "-" + let t = isTransientConnectionFlagSet ? "t" : "-" + let i = isInterventionRequiredFlagSet ? "i" : "-" + let C = isConnectionOnTrafficFlagSet ? "C" : "-" + let D = isConnectionOnDemandFlagSet ? "D" : "-" + let l = isLocalAddressFlagSet ? "l" : "-" + let d = isDirectFlagSet ? "d" : "-" + + return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)" + } +} + +fileprivate extension Reachability { + + func reachabilityChanged() { + guard previousFlags != flags else { return } + + let block = connection != .none ? whenReachable : whenUnreachable + + DispatchQueue.main.async { + block?(self) + self.notificationCenter.post(name: .reachabilityChanged, object:self) + } + + previousFlags = flags + } + + var isOnWWANFlagSet: Bool { + #if os(iOS) + return flags.contains(.isWWAN) + #else + return false + #endif + } + var isReachableFlagSet: Bool { + return flags.contains(.reachable) + } + var isConnectionRequiredFlagSet: Bool { + return flags.contains(.connectionRequired) + } + var isInterventionRequiredFlagSet: Bool { + return flags.contains(.interventionRequired) + } + var isConnectionOnTrafficFlagSet: Bool { + return flags.contains(.connectionOnTraffic) + } + var isConnectionOnDemandFlagSet: Bool { + return flags.contains(.connectionOnDemand) + } + var isConnectionOnTrafficOrDemandFlagSet: Bool { + return !flags.intersection([.connectionOnTraffic, .connectionOnDemand]).isEmpty + } + var isTransientConnectionFlagSet: Bool { + return flags.contains(.transientConnection) + } + var isLocalAddressFlagSet: Bool { + return flags.contains(.isLocalAddress) + } + var isDirectFlagSet: Bool { + return flags.contains(.isDirect) + } + var isConnectionRequiredAndTransientFlagSet: Bool { + return flags.intersection([.connectionRequired, .transientConnection]) == [.connectionRequired, .transientConnection] + } + + var flags: SCNetworkReachabilityFlags { + var flags = SCNetworkReachabilityFlags() + if SCNetworkReachabilityGetFlags(reachabilityRef, &flags) { + return flags + } else { + return SCNetworkReachabilityFlags() + } + } +} diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Photos/Photos.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Photos/Photos.swift new file mode 100644 index 0000000..ca99d18 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Photos/Photos.swift @@ -0,0 +1,258 @@ +import Foundation +import Photos + +@objc(Photos) +public class Photos : CAPPlugin { + static let DEFAULT_QUANTITY = 25 + static let DEFAULT_TYPES = "photos" + static let DEFAULT_THUMBNAIL_WIDTH = 256 + static let DEFAULT_THUMBNAIL_HEIGHT = 256 + + // Must be lazy here because it will prompt for permissions on instantiation without it + lazy var imageManager = PHCachingImageManager() + + @objc func getAlbums(_ call: CAPPluginCall) { + checkAuthorization(allowed: { + self.fetchAlbumsToJs(call) + }, notAllowed: { + call.error("Access to photos not allowed by user") + }) + } + + @objc func getPhotos(_ call: CAPPluginCall) { + checkAuthorization(allowed: { + self.fetchResultAssetsToJs(call) + }, notAllowed: { + call.error("Access to photos not allowed by user") + }) + } + + @objc func createAlbum(_ call: CAPPluginCall) { + guard let name = call.getString("name") else { + call.error("Must provide a name") + return + } + + checkAuthorization(allowed: { + PHPhotoLibrary.shared().performChanges({ + PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: name) + }, completionHandler: { success, error in + if !success { + call.error("Unable to create album", error) + return + } + call.success() + }) + }, notAllowed: { + call.error("Access to photos not allowed by user") + }) + } + + @objc func savePhoto(_ call: CAPPluginCall) { + guard let data = call.getString("data") else { + call.error("Must provide data as base64 encoded string") + return + } + + let albumId = call.getString("albumIdentifier") + + let dataDecoded : Data = Data(base64Encoded: data, options: .ignoreUnknownCharacters)! + guard let image = UIImage(data: dataDecoded) else { + call.error("Unable to load image from base64 data") + return + } + + var targetCollection: PHAssetCollection? + + if albumId != nil { + let albumFetchResult = PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [albumId!], options: nil) + albumFetchResult.enumerateObjects({ (collection, count, _) in + targetCollection = collection + }) + if targetCollection == nil { + call.error("Unable to find that album") + return + } + if !targetCollection!.canPerform(.addContent) { + call.error("Album doesn't support adding content (is this a smart album?)") + return + } + } + + checkAuthorization(allowed: { + // Add it to the photo library. + PHPhotoLibrary.shared().performChanges({ + let creationRequest = PHAssetChangeRequest.creationRequestForAsset(from: image) + + if let collection = targetCollection { + let addAssetRequest = PHAssetCollectionChangeRequest(for: collection) + addAssetRequest?.addAssets([creationRequest.placeholderForCreatedAsset!] as NSArray) + } + }, completionHandler: {success, error in + if !success { + call.error("Unable to save image to album", error) + } else { + call.success() + } + }) + }, notAllowed: { + call.error("Access to photos not allowed by user") + }) + } + + func checkAuthorization(allowed: @escaping () -> Void, notAllowed: @escaping () -> Void) { + let status = PHPhotoLibrary.authorizationStatus() + if status == PHAuthorizationStatus.authorized { + allowed() + } else { + PHPhotoLibrary.requestAuthorization({ (newStatus) in + if newStatus == PHAuthorizationStatus.authorized { + allowed() + } else { + notAllowed() + } + }) + } + } + + func fetchAlbumsToJs(_ call: CAPPluginCall) { + var albums = [JSObject]() + + let loadSharedAlbums = call.getBool("loadShared", defaultValue: false)! + + // Load our smart albums + var fetchResult = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: nil) + fetchResult.enumerateObjects({ (collection, count, stop: UnsafeMutablePointer) in + var o = JSObject() + o["name"] = collection.localizedTitle + o["identifier"] = collection.localIdentifier + o["type"] = "smart" + albums.append(o) + }) + + if loadSharedAlbums { + fetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumCloudShared, options: nil) + fetchResult.enumerateObjects({ (collection, count, stop: UnsafeMutablePointer) in + var o = JSObject() + o["name"] = collection.localizedTitle + o["identifier"] = collection.localIdentifier + o["type"] = "shared" + albums.append(o) + }) + } + + // Load our user albums + PHCollectionList.fetchTopLevelUserCollections(with: nil).enumerateObjects({ (collection, count, stop: UnsafeMutablePointer) in + var o = JSObject() + o["name"] = collection.localizedTitle + o["identifier"] = collection.localIdentifier + o["type"] = "user" + albums.append(o) + }) + + call.success([ + "albums": albums + ]) + } + + func fetchResultAssetsToJs(_ call: CAPPluginCall) { + var assets: [JSObject] = [] + + let albumId = call.getString("albumIdentifier") + let quantity = call.getInt("quantity", defaultValue: Photos.DEFAULT_QUANTITY)! + + var targetCollection: PHAssetCollection? + + let options = PHFetchOptions() + options.fetchLimit = quantity + options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)] + + if albumId != nil { + let albumFetchResult = PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [albumId!], options: nil) + albumFetchResult.enumerateObjects({ (collection, count, _) in + targetCollection = collection + }) + } + + var fetchResult: PHFetchResult; + if targetCollection != nil { + fetchResult = PHAsset.fetchAssets(in: targetCollection!, options: options) + } else { + fetchResult = PHAsset.fetchAssets(with: options) + } + + //let after = call.getString("after") + let types = call.getString("types") ?? Photos.DEFAULT_TYPES + let thumbnailWidth = call.getInt("thumbnailWidth", defaultValue: Photos.DEFAULT_THUMBNAIL_WIDTH)! + let thumbnailHeight = call.getInt("thumbnailHeight", defaultValue: Photos.DEFAULT_THUMBNAIL_HEIGHT)! + let thumbnailSize = CGSize(width: thumbnailWidth, height: thumbnailHeight) + let thumbnailQuality = call.getInt("thumbnailQuality", defaultValue: 95)! + + let requestOptions = PHImageRequestOptions() + requestOptions.isNetworkAccessAllowed = true + requestOptions.version = .current + requestOptions.deliveryMode = .opportunistic + requestOptions.isSynchronous = true + + fetchResult.enumerateObjects({ (asset, count: Int, stop: UnsafeMutablePointer) in + + if asset.mediaType == .image && types == "videos" { + return + } + if asset.mediaType == .video && types == "photos" { + return + } + + var a = JSObject() + + self.imageManager.requestImage(for: asset, targetSize: thumbnailSize, contentMode: .aspectFill, options: requestOptions, resultHandler: { (fetchedImage, _) in + guard let image = fetchedImage else { + return + } + + a["identifier"] = asset.localIdentifier + + // TODO: We need to know original type + a["data"] = UIImageJPEGRepresentation(image, CGFloat(thumbnailQuality) / 100.0)?.base64EncodedString() + + if asset.creationDate != nil { + a["creationDate"] = JSDate.toString(asset.creationDate!) + } + a["fullWidth"] = asset.pixelWidth + a["fullHeight"] = asset.pixelHeight + a["thumbnailWidth"] = image.size.width + a["thumbnailHeight"] = image.size.height + a["location"] = self.makeLocation(asset) + + assets.append(a) + }) + }) + + call.success([ + "photos": assets + ]) + } + + + func makeLocation(_ asset: PHAsset) -> JSObject { + var loc = JSObject() + guard let location = asset.location else { + return loc + } + + loc["latitude"] = location.coordinate.latitude + loc["longitude"] = location.coordinate.longitude + loc["altitude"] = location.altitude + loc["heading"] = location.course + loc["speed"] = location.speed + return loc + } + + /* + deinit { + PHPhotoLibrary.shared().unregisterChangeObserver(self) + } + */ +} + + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/PushNotifications.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/PushNotifications.swift new file mode 100644 index 0000000..fecc4ab --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/PushNotifications.swift @@ -0,0 +1 @@ +import Foundation diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Share.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Share.swift new file mode 100644 index 0000000..03f42f7 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Share.swift @@ -0,0 +1,52 @@ +import Foundation + +/** + * Implement three common modal types: alert, confirm, and prompt + */ +@objc(Share) +public class Share : CAPPlugin { + @objc func share(_ call: CAPPluginCall) { + var items = [Any]() + + if let text = call.options["text"] as? String { + items.append(text) + } + + if let url = call.options["url"] as? String { + let urlObj = URL(string: url) + items.append(urlObj!) + } + + let title = call.getString("title") + + if items.count == 0 { + call.error("Must provide at least url or message") + return + } + + DispatchQueue.main.async { + let actionController = UIActivityViewController(activityItems: items, applicationActivities: nil) + + if title != nil { + // https://stackoverflow.com/questions/17020288/how-to-set-a-mail-subject-in-uiactivityviewcontroller + actionController.setValue(title, forKey: "subject") + } + + actionController.completionWithItemsHandler = { (activityType, completed, _ returnedItems, activityError) in + if activityError != nil { + call.error("Error sharing item", activityError) + return + } + + // TODO: Support returnedItems + + call.success([ + "completed": completed, + "activityType": activityType?.rawValue ?? "" + ]) + } + + self.bridge.viewController.present(actionController, animated: true, completion: nil) + } + } +} diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/SplashScreen.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/SplashScreen.swift new file mode 100644 index 0000000..d7c2b79 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/SplashScreen.swift @@ -0,0 +1,136 @@ +import Foundation +import AudioToolbox + +@objc(SplashScreen) +public class SplashScreen : CAPPlugin { + var imageView = UIImageView() + var image: UIImage? + var call: CAPPluginCall? + var hideTask: Any? + var isVisible: Bool = false + + let launchShowDuration = 5000 + + let defaultFadeInDuration = 200 + let defaultFadeOutDuration = 200 + let defaultShowDuration = 3000 + let defaultAutoHide = true + + public override func load() { + buildViews() + } + + // Show the splash screen + @objc public func show(_ call: CAPPluginCall) { + self.call = call + + if image == nil { + call.error("No image named \"Splash\" found. Please check your Assets.xcassets for a file named Splash") + return + } + + let showDuration = call.get("showDuration", Int.self, defaultShowDuration)! + let fadeInDuration = call.get("fadeInDuration", Int.self, defaultFadeInDuration)! + let fadeOutDuration = call.get("fadeOutDuration", Int.self, defaultFadeOutDuration)! + let autoHide = call.get("autoHide", Bool.self, defaultAutoHide)! + + showSplash(showDuration: showDuration, fadeInDuration: fadeInDuration, fadeOutDuration: fadeOutDuration, autoHide: autoHide, completion: { + call.success() + }) + } + + // Hide the splash screen + @objc public func hide(_ call: CAPPluginCall) { + self.call = call + let fadeDuration = call.get("fadeOutDuration", Int.self, defaultFadeOutDuration)! + hideSplash(fadeOutDuration: fadeDuration) + call.success() + } + + func buildViews() { + // Find the image asset named "Splash" + // TODO: Find a way to not hard code this? + image = UIImage.init(named: "Splash") + + if image == nil { + print("Unable to find splash screen image. Make sure an image called Splash exists in your assets") + } + + // Observe for changes on fram and bounds to handle rotation resizing + let parentView = self.bridge.viewController.view + parentView?.addObserver(self, forKeyPath: "frame", options: .new, context: nil) + parentView?.addObserver(self, forKeyPath: "bounds", options: .new, context: nil) + + self.updateSplashImageBounds() + } + + func tearDown() { + self.isVisible = false + bridge.viewController.view.isUserInteractionEnabled = true + self.imageView.removeFromSuperview() + } + + // Update the bounds for the splash image. This will also be called when + // the parent view observers fire + func updateSplashImageBounds() { + guard let delegate = UIApplication.shared.delegate else { + bridge.modulePrint(self, "Unable to find root window object for SplashScreen bounds. Please file an issue") + return + } + + guard let window = delegate.window as? UIWindow else { + bridge.modulePrint(self, "Unable to find root window object for SplashScreen bounds. Please file an issue") + return + } + + imageView.alpha = 0 + imageView.image = image + imageView.frame = CGRect.init(origin: CGPoint.init(x: 0, y: 0), size: window.bounds.size) + imageView.contentMode = .scaleAspectFill + } + + public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + updateSplashImageBounds() + } + + func showOnLaunch() { + showSplash(showDuration: launchShowDuration, fadeInDuration: 0, fadeOutDuration: defaultFadeOutDuration, autoHide: true, completion: { + + }) + } + + func showSplash(showDuration: Int, fadeInDuration: Int, fadeOutDuration: Int, autoHide: Bool, completion: @escaping () -> Void) { + + DispatchQueue.main.async { + + self.bridge.viewController.view.addSubview(self.imageView) + + self.bridge.viewController.view.isUserInteractionEnabled = false + + UIView.transition(with: self.imageView, duration: TimeInterval(Double(fadeInDuration) / 1000), options: .curveLinear, animations: { + self.imageView.alpha = 1 + }) { (finished: Bool) in + self.isVisible = true + + if autoHide { + self.hideTask = DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + (Double(showDuration) / 1000), execute: { + self.hideSplash(fadeOutDuration: fadeOutDuration) + completion() + }) + } + } + } + } + + func hideSplash(fadeOutDuration: Int) { + if !isVisible { return } + DispatchQueue.main.async { + UIView.transition(with: self.imageView, duration: TimeInterval(Double(fadeOutDuration) / 1000), options: .curveLinear, animations: { + self.imageView.alpha = 0 + }) { (finished: Bool) in + self.tearDown() + } + } + } +} + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/StatusBar.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/StatusBar.swift new file mode 100644 index 0000000..463e5ee --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/StatusBar.swift @@ -0,0 +1,25 @@ +import Foundation + +/** + * StatusBar plugin. Requires "View controller-based status bar appearance" to + * be "NO" in Info.plist + */ +@objc(StatusBar) +public class StatusBar: CAPPlugin { + @objc public func setStyle(_ call: CAPPluginCall) { + let options = call.options! + + if let style = options["style"] as? String { + DispatchQueue.main.async { + if style == "DARK" { + UIApplication.shared.statusBarStyle = .lightContent + } else if style == "LIGHT" { + UIApplication.shared.statusBarStyle = .default + } + } + } + + call.success([:]) + } +} + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Toast.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Toast.swift new file mode 100644 index 0000000..1abc288 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/Plugins/Toast.swift @@ -0,0 +1,85 @@ +/* +import Foundation + +@IBDesignable class UIPaddingLabel: UILabel { + + private var _padding:CGFloat = 0.0; + + public var padding:CGFloat { + + get { return _padding; } + set { + _padding = newValue; + + paddingTop = _padding; + paddingLeft = _padding; + paddingBottom = _padding; + paddingRight = _padding; + } + } + + @IBInspectable var paddingTop:CGFloat = 0.0; + @IBInspectable var paddingLeft:CGFloat = 0.0; + @IBInspectable var paddingBottom:CGFloat = 0.0; + @IBInspectable var paddingRight:CGFloat = 0.0; + + override func drawText(in rect: CGRect) { + let insets = UIEdgeInsets(top:paddingTop, left:paddingLeft, bottom:paddingBottom, right:paddingRight); + super.drawText(in: UIEdgeInsetsInsetRect(rect, insets)); + } + + override var intrinsicContentSize: CGSize { + + get { + var intrinsicSuperViewContentSize = super.intrinsicContentSize; + intrinsicSuperViewContentSize.height += paddingTop + paddingBottom; + intrinsicSuperViewContentSize.width += paddingLeft + paddingRight; + return intrinsicSuperViewContentSize; + } + } +} + +@objc(Toast) +public class Toast : Plugin { + @objc func show(_ call: CAPPluginCall) { + guard let text = call.get("text", String.self) else { + call.error("text must be provided and must be a string.") + return + } + let durationStyle = call.get("durationStyle", String.self, "short")! + let duration = durationStyle == "short" ? 1000 : 5000 + + let toastLabel = UIPaddingLabel(); + toastLabel.padding = 10; + toastLabel.translatesAutoresizingMaskIntoConstraints = false; + toastLabel.backgroundColor = UIColor.darkGray; + toastLabel.textColor = UIColor.white; + toastLabel.textAlignment = .center; + toastLabel.text = text; + toastLabel.numberOfLines = 0; + toastLabel.alpha = 0.9; + toastLabel.layer.cornerRadius = 20; + toastLabel.clipsToBounds = true; + + let vc = self.bridge.viewController + + vc.view.addSubview(toastLabel); + + vc.view.addConstraint(NSLayoutConstraint(item:toastLabel, attribute:.left, relatedBy:.greaterThanOrEqual, toItem:self, attribute:.left, multiplier:1, constant:20)); + vc.view.addConstraint(NSLayoutConstraint(item:toastLabel, attribute:.right, relatedBy:.lessThanOrEqual, toItem:self, attribute:.right, multiplier:1, constant:-20)); + vc.view.addConstraint(NSLayoutConstraint(item:toastLabel, attribute:.bottom, relatedBy:.equal, toItem:self, attribute:.bottom, multiplier:1, constant:-20)); + vc.view.addConstraint(NSLayoutConstraint(item:toastLabel, attribute:.centerX, relatedBy:.equal, toItem:self, attribute:.centerX, multiplier:1, constant:0)); + + UIView.animate(withDuration:0.5, delay:Double(duration) / 1000.0, options:[], animations: { + + toastLabel.alpha = 0.0; + + }) { (Bool) in + + toastLabel.removeFromSuperview(); + } + } +} + */ + + diff --git a/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/UIColor.swift b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/UIColor.swift new file mode 100644 index 0000000..d507b60 --- /dev/null +++ b/ios/Plugin/Pods/Capacitor/ios/Capacitor/Capacitor/UIColor.swift @@ -0,0 +1,19 @@ +extension UIColor { + convenience init(fromHex hex: String) { + var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() + + if (cString.hasPrefix("#")) { + cString.remove(at: cString.startIndex) + } + + var rgbValue:UInt32 = 0 + Scanner(string: cString).scanHexInt32(&rgbValue) + + self.init( + red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, + green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, + blue: CGFloat(rgbValue & 0x0000FF) / 255.0, + alpha: CGFloat(1.0) + ) + } +} diff --git a/ios/Plugin/Pods/CapacitorCordova/LICENSE b/ios/Plugin/Pods/CapacitorCordova/LICENSE new file mode 100644 index 0000000..623c70a --- /dev/null +++ b/ios/Plugin/Pods/CapacitorCordova/LICENSE @@ -0,0 +1,23 @@ +Copyright 2015-present Drifty Co. +http://drifty.com/ + +MIT License + +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: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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. diff --git a/ios/Plugin/Pods/CapacitorCordova/README.md b/ios/Plugin/Pods/CapacitorCordova/README.md new file mode 100644 index 0000000..3710844 --- /dev/null +++ b/ios/Plugin/Pods/CapacitorCordova/README.md @@ -0,0 +1,44 @@ +*Capacitor is being actively developed and is not currently ready for public use. See our [Timeline](#timeline) for our upcoming plans and tentatively timeline* + +# ⚡️ Capacitor: Cross-platform apps with JavaScript and the Web ⚡️ + +Capacitor is a cross-platform API and code execution layer that makes it easy to call Native SDKs from web code and to write custom Native plugins that your app might need. Additionally, Capacitor provides first-class Progressive Web App support so you can write one app and deploy it to the app stores, _and_ the mobile web. + +Capacitor is being designed by the Ionic Framework team as an eventual alternative to Cordova, though backwards compatibility with Cordova plugins is a priority and is actively being worked on. Capacitor can be used without Ionic Framework, but soon it'll become a core part of the Ionic developer experience. + +Capacitor also comes with a Plugin API for building native plugins. On iOS, first-class Swift support is available, and much of the iOS Capacitor runtime is written in Swift. Plugins may also be written in Objective-C. On Android, support for writing plugins with Java and Kotlin is supported. + +Capacitor is still a work in progress and is not quite ready for use. Stay tuned for a public release in early 2018. + +## Timeline + +_Disclaimer: These dates are tentative. "It'll be ready when it's ready!"_ + +*Short term milestones* + + - November 2017 - Project Start + - January/Feb 2018 - Private alpha testing + - Feb 2018 - Public alpha + +## Roadmap + +_Disclaimer: Our roadmap is subject to change at any time and has no specific date guarantees_ + +2018 + + - __Cordova Plugin Integration__ + - Preliminary support for using plugins from the existing Cordova community + - __Native Shell Add-ons__ + - Support for interacting with Native UI shell elements, such as native menus, tabs, and navigation, with 1-1 fallbacks to the web for first-class Progressive Web App and Electron support. + - __Electron support__ + - Support for building Electron apps and interacting with Node.js libraries + - __Enterprise Premium Plugins__ + - Paid add-on plugins with support for common Enterprise use cases, such as storage, authentication, security, and more + - Developer Support options with SLAs and priority patches + - We are working with a few large teams/businesses as early development partners. Interested? Email [max@ionicframework.com](mailto:max@ionicframework.com) + +## Contributing + +Contributing to Capacitor may involve writing TypeScript, Swift/Obective-C, Java, or Markdown depending on the component you are working on. We are looking or help in any of those areas! + +Please read the [Contributing](.github/CONTRIBUTING.md) guide for more information. diff --git a/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/CapacitorCordova.h b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/CapacitorCordova.h new file mode 100644 index 0000000..5168bc3 --- /dev/null +++ b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/CapacitorCordova.h @@ -0,0 +1,17 @@ +#import + +//! Project version number for CapacitorCordova. +FOUNDATION_EXPORT double CapacitorCordovaVersionNumber; + +//! Project version string for CapacitorCordova. +FOUNDATION_EXPORT const unsigned char CapacitorCordovaVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +#import +#import +#import +#import +#import +#import +#import diff --git a/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDV.h b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDV.h new file mode 100644 index 0000000..505b0e5 --- /dev/null +++ b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDV.h @@ -0,0 +1,24 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVAvailability.h" +#import "CDVPlugin.h" +#import "CDVPluginResult.h" +#import "CDVCommandDelegate.h" +#import "CDVInvokedUrlCommand.h" diff --git a/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVAvailability.h b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVAvailability.h new file mode 100644 index 0000000..cc4c8fc --- /dev/null +++ b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVAvailability.h @@ -0,0 +1,109 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#define __CORDOVA_IOS__ + +#define __CORDOVA_0_9_6 906 +#define __CORDOVA_1_0_0 10000 +#define __CORDOVA_1_1_0 10100 +#define __CORDOVA_1_2_0 10200 +#define __CORDOVA_1_3_0 10300 +#define __CORDOVA_1_4_0 10400 +#define __CORDOVA_1_4_1 10401 +#define __CORDOVA_1_5_0 10500 +#define __CORDOVA_1_6_0 10600 +#define __CORDOVA_1_6_1 10601 +#define __CORDOVA_1_7_0 10700 +#define __CORDOVA_1_8_0 10800 +#define __CORDOVA_1_8_1 10801 +#define __CORDOVA_1_9_0 10900 +#define __CORDOVA_2_0_0 20000 +#define __CORDOVA_2_1_0 20100 +#define __CORDOVA_2_2_0 20200 +#define __CORDOVA_2_3_0 20300 +#define __CORDOVA_2_4_0 20400 +#define __CORDOVA_2_5_0 20500 +#define __CORDOVA_2_6_0 20600 +#define __CORDOVA_2_7_0 20700 +#define __CORDOVA_2_8_0 20800 +#define __CORDOVA_2_9_0 20900 +#define __CORDOVA_3_0_0 30000 +#define __CORDOVA_3_1_0 30100 +#define __CORDOVA_3_2_0 30200 +#define __CORDOVA_3_3_0 30300 +#define __CORDOVA_3_4_0 30400 +#define __CORDOVA_3_4_1 30401 +#define __CORDOVA_3_5_0 30500 +#define __CORDOVA_3_6_0 30600 +#define __CORDOVA_3_7_0 30700 +#define __CORDOVA_3_8_0 30800 +#define __CORDOVA_3_9_0 30900 +#define __CORDOVA_3_9_1 30901 +#define __CORDOVA_3_9_2 30902 +#define __CORDOVA_4_0_0 40000 +#define __CORDOVA_4_0_1 40001 +#define __CORDOVA_4_1_0 40100 +#define __CORDOVA_4_1_1 40101 +#define __CORDOVA_4_2_0 40200 +#define __CORDOVA_4_2_1 40201 +#define __CORDOVA_4_3_0 40300 +#define __CORDOVA_4_3_1 40301 +#define __CORDOVA_4_4_0 40400 +#define __CORDOVA_4_5_0 40500 +#define __CORDOVA_4_5_1 40501 +#define __CORDOVA_4_5_2 40502 +#define __CORDOVA_4_5_4 40504 +/* coho:next-version,insert-before */ +#define __CORDOVA_NA 99999 /* not available */ + +/* + #if CORDOVA_VERSION_MIN_REQUIRED >= __CORDOVA_4_0_0 + // do something when its at least 4.0.0 + #else + // do something else (non 4.0.0) + #endif + */ +#ifndef CORDOVA_VERSION_MIN_REQUIRED + /* coho:next-version-min-required,replace-after */ + #define CORDOVA_VERSION_MIN_REQUIRED __CORDOVA_4_5_4 +#endif + +/* + Returns YES if it is at least version specified as NSString(X) + Usage: + if (IsAtLeastiOSVersion(@"5.1")) { + // do something for iOS 5.1 or greater + } + */ +#define IsAtLeastiOSVersion(X) ([[[UIDevice currentDevice] systemVersion] compare:X options:NSNumericSearch] != NSOrderedAscending) + +/* Return the string version of the decimal version */ +#define CDV_VERSION [NSString stringWithFormat:@"%d.%d.%d", \ + (CORDOVA_VERSION_MIN_REQUIRED / 10000), \ + (CORDOVA_VERSION_MIN_REQUIRED % 10000) / 100, \ + (CORDOVA_VERSION_MIN_REQUIRED % 10000) % 100] + +// Enable this to log all exec() calls. +#define CDV_ENABLE_EXEC_LOGGING 0 +#if CDV_ENABLE_EXEC_LOGGING + #define CDV_EXEC_LOG NSLog +#else + #define CDV_EXEC_LOG(...) do { \ +} while (NO) +#endif diff --git a/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegate.h b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegate.h new file mode 100644 index 0000000..aa9add7 --- /dev/null +++ b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegate.h @@ -0,0 +1,49 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVInvokedUrlCommand.h" + +@class CDVPlugin; +@class CDVPluginResult; + +typedef NSURL* (^ UrlTransformerBlock)(NSURL*); + +@protocol CDVCommandDelegate + +@property (nonatomic, readonly) NSDictionary* settings; +@property (nonatomic, copy) UrlTransformerBlock urlTransformer; + +- (NSString*)pathForResource:(NSString*)resourcepath; +- (id)getCommandInstance:(NSString*)pluginName; + +// Sends a plugin result to the JS. This is thread-safe. +- (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId; +// Evaluates the given JS. This is thread-safe. +- (void)evalJs:(NSString*)js; +// Can be used to evaluate JS right away instead of scheduling it on the run-loop. +// This is required for dispatch resign and pause events, but should not be used +// without reason. Without the run-loop delay, alerts used in JS callbacks may result +// in dead-lock. This method must be called from the UI thread. +- (void)evalJs:(NSString*)js scheduledOnRunLoop:(BOOL)scheduledOnRunLoop; +// Runs the given block on a background thread using a shared thread-pool. +- (void)runInBackground:(void (^)())block; +// Returns the User-Agent of the associated UIWebView. +- (NSString*)userAgent; + +@end diff --git a/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.h b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.h new file mode 100644 index 0000000..d25a4fc --- /dev/null +++ b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.h @@ -0,0 +1,37 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import +#import "CDVCommandDelegate.h" +#import + +@class CDVViewController; +@class CDVCommandQueue; + +@interface CDVCommandDelegateImpl : NSObject { + @private + __weak WKWebView* _webView; + NSRegularExpression* _callbackIdPattern; + @protected + __weak CDVCommandQueue* _commandQueue; + BOOL _delayResponses; +} +- (id)initWithWebView:(WKWebView*)webView; +- (void)flushCommandQueueWithDelayedJs; +@end diff --git a/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.m b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.m new file mode 100644 index 0000000..e0df7ea --- /dev/null +++ b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.m @@ -0,0 +1,147 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVCommandDelegateImpl.h" +#import "CDVPluginResult.h" +#import + +@implementation CDVCommandDelegateImpl + +@synthesize urlTransformer; + +- (id)initWithWebView:(WKWebView*)webView +{ + self = [super init]; + if (self != nil) { + _webView = webView; + + NSError* err = nil; + _callbackIdPattern = [NSRegularExpression regularExpressionWithPattern:@"[^A-Za-z0-9._-]" options:0 error:&err]; + if (err != nil) { + // Couldn't initialize Regex + NSLog(@"Error: Couldn't initialize regex"); + _callbackIdPattern = nil; + } + } + return self; +} + +- (NSString*)pathForResource:(NSString*)resourcepath +{ + NSBundle* mainBundle = [NSBundle mainBundle]; + NSMutableArray* directoryParts = [NSMutableArray arrayWithArray:[resourcepath componentsSeparatedByString:@"/"]]; + NSString* filename = [directoryParts lastObject]; + + [directoryParts removeLastObject]; + + NSString* directoryPartsJoined = [directoryParts componentsJoinedByString:@"/"]; + + + return [mainBundle pathForResource:filename ofType:@"" inDirectory:@"www"]; +} + +- (void)flushCommandQueueWithDelayedJs +{ + _delayResponses = YES; + _delayResponses = NO; +} + +- (void)evalJsHelper2:(NSString*)js +{ + [_webView evaluateJavaScript:js completionHandler:^(id obj, NSError* error) { + // TODO: obj can be something other than string + if ([obj isKindOfClass:[NSString class]]) { + NSString* commandsJSON = (NSString*)obj; + if ([commandsJSON length] > 0) { + NSLog(@"Exec: Retrieved new exec messages by chaining."); + } + } + }]; +} + +- (BOOL)isValidCallbackId:(NSString*)callbackId +{ + if ((callbackId == nil) || (_callbackIdPattern == nil)) { + return NO; + } + + // Disallow if too long or if any invalid characters were found. + if (([callbackId length] > 100) || [_callbackIdPattern firstMatchInString:callbackId options:0 range:NSMakeRange(0, [callbackId length])]) { + return NO; + } + return YES; +} + +- (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId +{ + // This occurs when there is are no win/fail callbacks for the call. + if ([@"INVALID" isEqualToString:callbackId]) { + return; + } + // This occurs when the callback id is malformed. + if (![self isValidCallbackId:callbackId]) { + NSLog(@"Invalid callback id received by sendPluginResult"); + return; + } + int status = [result.status intValue]; + BOOL keepCallback = [result.keepCallback boolValue]; + NSString* argumentsAsJSON = [result argumentsAsJSON]; + BOOL debug = NO; + +#ifdef DEBUG + debug = YES; +#endif + + NSString* js = [NSString stringWithFormat:@"cordova.require('cordova/exec').nativeCallback('%@',%d,%@,%d, %d)", callbackId, status, argumentsAsJSON, keepCallback, debug]; + + [self evalJsHelper2:js]; +} + +- (void)evalJs:(NSString*)js +{ + [self evalJs:js scheduledOnRunLoop:YES]; +} + +- (void)evalJs:(NSString*)js scheduledOnRunLoop:(BOOL)scheduledOnRunLoop +{ + js = [NSString stringWithFormat:@"try{cordova.require('cordova/exec').nativeEvalAndFetch(function(){%@})}catch(e){console.log('exception nativeEvalAndFetch : '+e);};", js]; + [self evalJsHelper2:js]; +} + +- (id)getCommandInstance:(NSString*)pluginName +{ + return nil; +} + +- (void)runInBackground:(void (^)())block +{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); +} + +- (NSString*)userAgent +{ + return nil; +} + +- (NSDictionary*)settings +{ + return nil; +} + +@end diff --git a/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.h b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.h new file mode 100644 index 0000000..993e0a2 --- /dev/null +++ b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.h @@ -0,0 +1,52 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import + +@interface CDVInvokedUrlCommand : NSObject { + NSString* _callbackId; + NSString* _className; + NSString* _methodName; + NSArray* _arguments; +} + +@property (nonatomic, readonly) NSArray* arguments; +@property (nonatomic, readonly) NSString* callbackId; +@property (nonatomic, readonly) NSString* className; +@property (nonatomic, readonly) NSString* methodName; + ++ (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry; + +- (id)initWithArguments:(NSArray*)arguments + callbackId:(NSString*)callbackId + className:(NSString*)className + methodName:(NSString*)methodName; + +- (id)initFromJson:(NSArray*)jsonEntry; + +// Returns the argument at the given index. +// If index >= the number of arguments, returns nil. +// If the argument at the given index is NSNull, returns nil. +- (id)argumentAtIndex:(NSUInteger)index; +// Same as above, but returns defaultValue instead of nil. +- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue; +// Same as above, but returns defaultValue instead of nil, and if the argument is not of the expected class, returns defaultValue +- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass; + +@end diff --git a/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.m b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.m new file mode 100644 index 0000000..fd42dd1 --- /dev/null +++ b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.m @@ -0,0 +1,116 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVInvokedUrlCommand.h" + + +@implementation CDVInvokedUrlCommand + +@synthesize arguments = _arguments; +@synthesize callbackId = _callbackId; +@synthesize className = _className; +@synthesize methodName = _methodName; + ++ (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry +{ + return [[CDVInvokedUrlCommand alloc] initFromJson:jsonEntry]; +} + +- (id)initFromJson:(NSArray*)jsonEntry +{ + id tmp = [jsonEntry objectAtIndex:0]; + NSString* callbackId = tmp == [NSNull null] ? nil : tmp; + NSString* className = [jsonEntry objectAtIndex:1]; + NSString* methodName = [jsonEntry objectAtIndex:2]; + NSMutableArray* arguments = [jsonEntry objectAtIndex:3]; + + return [self initWithArguments:arguments + callbackId:callbackId + className:className + methodName:methodName]; +} + +- (id)initWithArguments:(NSArray*)arguments + callbackId:(NSString*)callbackId + className:(NSString*)className + methodName:(NSString*)methodName +{ + self = [super init]; + if (self != nil) { + _arguments = arguments; + _callbackId = callbackId; + _className = className; + _methodName = methodName; + } + [self massageArguments]; + return self; +} + +- (void)massageArguments +{ + NSMutableArray* newArgs = nil; + + for (NSUInteger i = 0, count = [_arguments count]; i < count; ++i) { + id arg = [_arguments objectAtIndex:i]; + if (![arg isKindOfClass:[NSDictionary class]]) { + continue; + } + NSDictionary* dict = arg; + NSString* type = [dict objectForKey:@"CDVType"]; + if (!type || ![type isEqualToString:@"ArrayBuffer"]) { + continue; + } + NSString* data = [dict objectForKey:@"data"]; + if (!data) { + continue; + } + if (newArgs == nil) { + newArgs = [NSMutableArray arrayWithArray:_arguments]; + _arguments = newArgs; + } + [newArgs replaceObjectAtIndex:i withObject:[[NSData alloc] initWithBase64EncodedString:data options:0]]; + } +} + +- (id)argumentAtIndex:(NSUInteger)index +{ + return [self argumentAtIndex:index withDefault:nil]; +} + +- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue +{ + return [self argumentAtIndex:index withDefault:defaultValue andClass:nil]; +} + +- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass +{ + if (index >= [_arguments count]) { + return defaultValue; + } + id ret = [_arguments objectAtIndex:index]; + if (ret == [NSNull null]) { + ret = defaultValue; + } + if ((aClass != nil) && ![ret isKindOfClass:aClass]) { + ret = defaultValue; + } + return ret; +} + +@end diff --git a/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.h b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.h new file mode 100644 index 0000000..f12cbab --- /dev/null +++ b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.h @@ -0,0 +1,78 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import +#import +#import "CDVPluginResult.h" +#import "CDVCommandDelegate.h" +#import "CDVAvailability.h" + +@interface UIView (org_apache_cordova_UIView_Extension) + +@property (nonatomic, weak) UIScrollView* scrollView; + +@end + +extern NSString* const CDVPageDidLoadNotification; +extern NSString* const CDVPluginHandleOpenURLNotification; +extern NSString* const CDVPluginResetNotification; +extern NSString* const CDVViewWillAppearNotification; +extern NSString* const CDVViewDidAppearNotification; +extern NSString* const CDVViewWillDisappearNotification; +extern NSString* const CDVViewDidDisappearNotification; +extern NSString* const CDVViewWillLayoutSubviewsNotification; +extern NSString* const CDVViewDidLayoutSubviewsNotification; +extern NSString* const CDVViewWillTransitionToSizeNotification; + +/* + * The local and remote push notification functionality has been removed from the core in cordova-ios 4.x, + * but these constants have unfortunately have not been removed, but will be removed in 5.x. + * + * To have the same functionality as 3.x, use a third-party plugin or the experimental + * https://github.com/apache/cordova-plugins/tree/master/notification-rebroadcast + */ + + +@interface CDVPlugin : NSObject {} + +@property (nonatomic, weak) UIView* webView; +@property (nonatomic, weak) id webViewEngine; + +@property (nonatomic, weak) UIViewController* viewController; +@property (nonatomic, strong) id commandDelegate; + +- (void)pluginInitialize; + +- (void)handleOpenURL:(NSNotification*)notification; +- (void)onAppTerminate; +- (void)onMemoryWarning; +- (void)onReset; +- (void)dispose; + +/* + // see initWithWebView implementation + - (void) onPause {} + - (void) onResume {} + - (void) onOrientationWillChange {} + - (void) onOrientationDidChange {} + */ + +- (id)appDelegate; + +@end diff --git a/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.m b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.m new file mode 100644 index 0000000..e5df5b4 --- /dev/null +++ b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.m @@ -0,0 +1,154 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVPlugin.h" +#include + +@implementation UIView (org_apache_cordova_UIView_Extension) + +@dynamic scrollView; + +- (UIScrollView*)scrollView +{ + SEL scrollViewSelector = NSSelectorFromString(@"scrollView"); + + if ([self respondsToSelector:scrollViewSelector]) { + return ((id (*)(id, SEL))objc_msgSend)(self, scrollViewSelector); + } + + return nil; +} + +@end + +NSString* const CDVPageDidLoadNotification = @"CDVPageDidLoadNotification"; +NSString* const CDVPluginHandleOpenURLNotification = @"CDVPluginHandleOpenURLNotification"; +NSString* const CDVPluginResetNotification = @"CDVPluginResetNotification"; +NSString* const CDVLocalNotification = @"CDVLocalNotification"; +NSString* const CDVRemoteNotification = @"CDVRemoteNotification"; +NSString* const CDVRemoteNotificationError = @"CDVRemoteNotificationError"; +NSString* const CDVViewWillAppearNotification = @"CDVViewWillAppearNotification"; +NSString* const CDVViewDidAppearNotification = @"CDVViewDidAppearNotification"; +NSString* const CDVViewWillDisappearNotification = @"CDVViewWillDisappearNotification"; +NSString* const CDVViewDidDisappearNotification = @"CDVViewDidDisappearNotification"; +NSString* const CDVViewWillLayoutSubviewsNotification = @"CDVViewWillLayoutSubviewsNotification"; +NSString* const CDVViewDidLayoutSubviewsNotification = @"CDVViewDidLayoutSubviewsNotification"; +NSString* const CDVViewWillTransitionToSizeNotification = @"CDVViewWillTransitionToSizeNotification"; + +@interface CDVPlugin () + +@property (readwrite, assign) BOOL hasPendingOperation; + +@end + +@implementation CDVPlugin +@synthesize webViewEngine, viewController, commandDelegate, hasPendingOperation, webView; + +// Do not override these methods. Use pluginInitialize instead. +- (instancetype)initWithWebViewEngine:(id)theWebViewEngine +{ + self = [super init]; + if (self) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppTerminate) name:UIApplicationWillTerminateNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:CDVPluginHandleOpenURLNotification object:nil]; + + self.webViewEngine = theWebViewEngine; + } + return self; +} + +- (void)pluginInitialize +{ + // You can listen to more app notifications, see: + // http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4 + + // NOTE: if you want to use these, make sure you uncomment the corresponding notification handler + + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; + + // Added in 2.5.0 + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad:) name:CDVPageDidLoadNotification object:self.webView]; + //Added in 4.3.0 + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillAppear:) name:CDVViewWillAppearNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidAppear:) name:CDVViewDidAppearNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillDisappear:) name:CDVViewWillDisappearNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidDisappear:) name:CDVViewDidDisappearNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillLayoutSubviews:) name:CDVViewWillLayoutSubviewsNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidLayoutSubviews:) name:CDVViewDidLayoutSubviewsNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillTransitionToSize:) name:CDVViewWillTransitionToSizeNotification object:nil]; +} + +- (void)dispose +{ + viewController = nil; + commandDelegate = nil; +} + +/* +// NOTE: for onPause and onResume, calls into JavaScript must not call or trigger any blocking UI, like alerts +- (void) onPause {} +- (void) onResume {} +- (void) onOrientationWillChange {} +- (void) onOrientationDidChange {} +*/ + +/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */ +- (void)handleOpenURL:(NSNotification*)notification +{ + // override to handle urls sent to your app + // register your url schemes in your App-Info.plist + + NSURL* url = [notification object]; + + if ([url isKindOfClass:[NSURL class]]) { + /* Do your thing! */ + } +} + +/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */ +- (void)onAppTerminate +{ + // override this if you need to do any cleanup on app exit +} + +- (void)onMemoryWarning +{ + // override to remove caches, etc +} + +- (void)onReset +{ + // Override to cancel any long-running requests when the WebView navigates or refreshes. +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; // this will remove all notifications unless added using addObserverForName:object:queue:usingBlock: +} + +- (id)appDelegate +{ + return [[UIApplication sharedApplication] delegate]; +} + +@end diff --git a/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.h b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.h new file mode 100644 index 0000000..78a29fb --- /dev/null +++ b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.h @@ -0,0 +1,65 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import + +typedef enum { + CDVCommandStatus_NO_RESULT = 0, + CDVCommandStatus_OK, + CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION, + CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION, + CDVCommandStatus_INSTANTIATION_EXCEPTION, + CDVCommandStatus_MALFORMED_URL_EXCEPTION, + CDVCommandStatus_IO_EXCEPTION, + CDVCommandStatus_INVALID_ACTION, + CDVCommandStatus_JSON_EXCEPTION, + CDVCommandStatus_ERROR +} CDVCommandStatus; + +@interface CDVPluginResult : NSObject {} + +@property (nonatomic, strong, readonly) NSNumber* status; +@property (nonatomic, strong, readonly) id message; +@property (nonatomic, strong) NSNumber* keepCallback; +// This property can be used to scope the lifetime of another object. For example, +// Use it to store the associated NSData when `message` is created using initWithBytesNoCopy. +@property (nonatomic, strong) id associatedObject; + +- (CDVPluginResult*)init; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSInteger:(NSInteger)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSUInteger:(NSUInteger)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages; ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode; + ++ (void)setVerbose:(BOOL)verbose; ++ (BOOL)isVerbose; + +- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback; + +- (NSString*)argumentsAsJSON; + +@end diff --git a/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.m b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.m new file mode 100644 index 0000000..4d46648 --- /dev/null +++ b/ios/Plugin/Pods/CapacitorCordova/ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.m @@ -0,0 +1,199 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVPluginResult.h" + +@interface CDVPluginResult () + +- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage; + +@end + +@implementation CDVPluginResult +@synthesize status, message, keepCallback, associatedObject; + +static NSArray* org_apache_cordova_CommandStatusMsgs; + +id messageFromArrayBuffer(NSData* data) +{ + return @{ + @"CDVType" : @"ArrayBuffer", + @"data" :[data base64EncodedStringWithOptions:0] + }; +} + +id massageMessage(id message) +{ + if ([message isKindOfClass:[NSData class]]) { + return messageFromArrayBuffer(message); + } + return message; +} + +id messageFromMultipart(NSArray* theMessages) +{ + NSMutableArray* messages = [NSMutableArray arrayWithArray:theMessages]; + + for (NSUInteger i = 0; i < messages.count; ++i) { + [messages replaceObjectAtIndex:i withObject:massageMessage([messages objectAtIndex:i])]; + } + + return @{ + @"CDVType" : @"MultiPart", + @"messages" : messages + }; +} + ++ (void)initialize +{ + org_apache_cordova_CommandStatusMsgs = [[NSArray alloc] initWithObjects:@"No result", + @"OK", + @"Class not found", + @"Illegal access", + @"Instantiation error", + @"Malformed url", + @"IO error", + @"Invalid action", + @"JSON error", + @"Error", + nil]; +} + +- (CDVPluginResult*)init +{ + return [self initWithStatus:CDVCommandStatus_NO_RESULT message:nil]; +} + +- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage +{ + self = [super init]; + if (self) { + status = [NSNumber numberWithInt:statusOrdinal]; + message = theMessage; + keepCallback = [NSNumber numberWithBool:NO]; + } + return self; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal +{ + return [[self alloc] initWithStatus:statusOrdinal message:nil]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:theMessage]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:theMessage]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithInt:theMessage]]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSInteger:(NSInteger)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithInteger:theMessage]]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsNSUInteger:(NSUInteger)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithUnsignedInteger:theMessage]]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithDouble:theMessage]]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithBool:theMessage]]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:theMessage]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage +{ + return [[self alloc] initWithStatus:statusOrdinal message:messageFromArrayBuffer(theMessage)]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages +{ + return [[self alloc] initWithStatus:statusOrdinal message:messageFromMultipart(theMessages)]; +} + ++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode +{ + NSDictionary* errDict = @{@"code" :[NSNumber numberWithInt:errorCode]}; + + return [[self alloc] initWithStatus:statusOrdinal message:errDict]; +} + +- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback +{ + [self setKeepCallback:[NSNumber numberWithBool:bKeepCallback]]; +} + +- (NSString*)argumentsAsJSON +{ + id arguments = (self.message == nil ? [NSNull null] : self.message); + NSArray* argumentsWrappedInArray = [NSArray arrayWithObject:arguments]; + + NSString* argumentsJSON = [self JSONStringFromArray:argumentsWrappedInArray]; + + argumentsJSON = [argumentsJSON substringWithRange:NSMakeRange(1, [argumentsJSON length] - 2)]; + + return argumentsJSON; +} + +static BOOL gIsVerbose = NO; ++ (void)setVerbose:(BOOL)verbose +{ + gIsVerbose = verbose; +} + ++ (BOOL)isVerbose +{ + return gIsVerbose; +} + +- (NSString*)JSONStringFromArray:(NSArray *) array +{ + NSError* error = nil; + NSData* jsonData = [NSJSONSerialization dataWithJSONObject:array + options:0 + error:&error]; + + if (error != nil) { + NSLog(@"NSArray JSONString error: %@", [error localizedDescription]); + return nil; + } else { + return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + } +} + +@end diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServer.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServer.h new file mode 100644 index 0000000..59572ba --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServer.h @@ -0,0 +1,622 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#import "GCDWebServerRequest.h" +#import "GCDWebServerResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * The GCDWebServerMatchBlock is called for every handler added to the + * GCDWebServer whenever a new HTTP request has started (i.e. HTTP headers have + * been received). The block is passed the basic info for the request (HTTP method, + * URL, headers...) and must decide if it wants to handle it or not. + * + * If the handler can handle the request, the block must return a new + * GCDWebServerRequest instance created with the same basic info. + * Otherwise, it simply returns nil. + */ +typedef GCDWebServerRequest* _Nullable (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery); + +/** + * The GCDWebServerProcessBlock is called after the HTTP request has been fully + * received (i.e. the entire HTTP body has been read). The block is passed the + * GCDWebServerRequest created at the previous step by the GCDWebServerMatchBlock. + * + * The block must return a GCDWebServerResponse or nil on error, which will + * result in a 500 HTTP status code returned to the client. It's however + * recommended to return a GCDWebServerErrorResponse on error so more useful + * information can be returned to the client. + */ +typedef GCDWebServerResponse* _Nullable (^GCDWebServerProcessBlock)(__kindof GCDWebServerRequest* request); + +/** + * The GCDWebServerAsynchronousProcessBlock works like the GCDWebServerProcessBlock + * except the GCDWebServerResponse can be returned to the server at a later time + * allowing for asynchronous generation of the response. + * + * The block must eventually call "completionBlock" passing a GCDWebServerResponse + * or nil on error, which will result in a 500 HTTP status code returned to the client. + * It's however recommended to return a GCDWebServerErrorResponse on error so more + * useful information can be returned to the client. + */ +typedef void (^GCDWebServerCompletionBlock)(GCDWebServerResponse* _Nullable response); +typedef void (^GCDWebServerAsyncProcessBlock)(__kindof GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock); + +/** + * The port used by the GCDWebServer (NSNumber / NSUInteger). + * + * The default value is 0 i.e. let the OS pick a random port. + */ +extern NSString* const GCDWebServerOption_Port; + +/** + * The Bonjour name used by the GCDWebServer (NSString). If set to an empty string, + * the name will automatically take the value of the GCDWebServerOption_ServerName + * option. If this option is set to nil, Bonjour will be disabled. + * + * The default value is nil. + */ +extern NSString* const GCDWebServerOption_BonjourName; + +/** + * The Bonjour service type used by the GCDWebServer (NSString). + * + * The default value is "_http._tcp", the service type for HTTP web servers. + */ +extern NSString* const GCDWebServerOption_BonjourType; + +/** + * Request a port mapping in the NAT gateway (NSNumber / BOOL). + * + * This uses the DNSService API under the hood which supports IPv4 mappings only. + * + * The default value is NO. + * + * @warning The external port set up by the NAT gateway may be different than + * the one used by the GCDWebServer. + */ +extern NSString* const GCDWebServerOption_RequestNATPortMapping; + +/** + * Only accept HTTP requests coming from localhost i.e. not from the outside + * network (NSNumber / BOOL). + * + * The default value is NO. + * + * @warning Bonjour and NAT port mapping should be disabled if using this option + * since the server will not be reachable from the outside network anyway. + */ +extern NSString* const GCDWebServerOption_BindToLocalhost; + +/** + * The maximum number of incoming HTTP requests that can be queued waiting to + * be handled before new ones are dropped (NSNumber / NSUInteger). + * + * The default value is 16. + */ +extern NSString* const GCDWebServerOption_MaxPendingConnections; + +/** + * The value for "Server" HTTP header used by the GCDWebServer (NSString). + * + * The default value is the GCDWebServer class name. + */ +extern NSString* const GCDWebServerOption_ServerName; + +/** + * The authentication method used by the GCDWebServer + * (one of "GCDWebServerAuthenticationMethod_..."). + * + * The default value is nil i.e. authentication is disabled. + */ +extern NSString* const GCDWebServerOption_AuthenticationMethod; + +/** + * The authentication realm used by the GCDWebServer (NSString). + * + * The default value is the same as the GCDWebServerOption_ServerName option. + */ +extern NSString* const GCDWebServerOption_AuthenticationRealm; + +/** + * The authentication accounts used by the GCDWebServer + * (NSDictionary of username / password pairs). + * + * The default value is nil i.e. no accounts. + */ +extern NSString* const GCDWebServerOption_AuthenticationAccounts; + +/** + * The class used by the GCDWebServer when instantiating GCDWebServerConnection + * (subclass of GCDWebServerConnection). + * + * The default value is the GCDWebServerConnection class. + */ +extern NSString* const GCDWebServerOption_ConnectionClass; + +/** + * Allow the GCDWebServer to pretend "HEAD" requests are actually "GET" ones + * and automatically discard the HTTP body of the response (NSNumber / BOOL). + * + * The default value is YES. + */ +extern NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET; + +/** + * The interval expressed in seconds used by the GCDWebServer to decide how to + * coalesce calls to -webServerDidConnect: and -webServerDidDisconnect: + * (NSNumber / double). Coalescing will be disabled if the interval is <= 0.0. + * + * The default value is 1.0 second. + */ +extern NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval; + +/** + * Set the dispatch queue priority on which server connection will be + * run (NSNumber / long). + * + * + * The default value is DISPATCH_QUEUE_PRIORITY_DEFAULT. + */ +extern NSString* const GCDWebServerOption_DispatchQueuePriority; + +#if TARGET_OS_IPHONE + +/** + * Enables the GCDWebServer to automatically suspend itself (as if -stop was + * called) when the iOS app goes into the background and the last + * GCDWebServerConnection is closed, then resume itself (as if -start was called) + * when the iOS app comes back to the foreground (NSNumber / BOOL). + * + * See the README.md file for more information about this option. + * + * The default value is YES. + * + * @warning The running property will be NO while the GCDWebServer is suspended. + */ +extern NSString* const GCDWebServerOption_AutomaticallySuspendInBackground; + +#endif + +/** + * HTTP Basic Authentication scheme (see https://tools.ietf.org/html/rfc2617). + * + * @warning Use of this authentication scheme is not recommended as the + * passwords are sent in clear. + */ +extern NSString* const GCDWebServerAuthenticationMethod_Basic; + +/** + * HTTP Digest Access Authentication scheme (see https://tools.ietf.org/html/rfc2617). + */ +extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess; + +@class GCDWebServer; + +/** + * Delegate methods for GCDWebServer. + * + * @warning These methods are always called on the main thread in a serialized way. + */ +@protocol GCDWebServerDelegate +@optional + +/** + * This method is called after the server has successfully started. + */ +- (void)webServerDidStart:(GCDWebServer*)server; + +/** + * This method is called after the Bonjour registration for the server has + * successfully completed. + * + * Use the "bonjourServerURL" property to retrieve the Bonjour address of the + * server. + */ +- (void)webServerDidCompleteBonjourRegistration:(GCDWebServer*)server; + +/** + * This method is called after the NAT port mapping for the server has been + * updated. + * + * Use the "publicServerURL" property to retrieve the public address of the + * server. + */ +- (void)webServerDidUpdateNATPortMapping:(GCDWebServer*)server; + +/** + * This method is called when the first GCDWebServerConnection is opened by the + * server to serve a series of HTTP requests. + * + * A series of HTTP requests is considered ongoing as long as new HTTP requests + * keep coming (and new GCDWebServerConnection instances keep being opened), + * until before the last HTTP request has been responded to (and the + * corresponding last GCDWebServerConnection closed). + */ +- (void)webServerDidConnect:(GCDWebServer*)server; + +/** + * This method is called when the last GCDWebServerConnection is closed after + * the server has served a series of HTTP requests. + * + * The GCDWebServerOption_ConnectedStateCoalescingInterval option can be used + * to have the server wait some extra delay before considering that the series + * of HTTP requests has ended (in case there some latency between consecutive + * requests). This effectively coalesces the calls to -webServerDidConnect: + * and -webServerDidDisconnect:. + */ +- (void)webServerDidDisconnect:(GCDWebServer*)server; + +/** + * This method is called after the server has stopped. + */ +- (void)webServerDidStop:(GCDWebServer*)server; + +@end + +/** + * The GCDWebServer class listens for incoming HTTP requests on a given port, + * then passes each one to a "handler" capable of generating an HTTP response + * for it, which is then sent back to the client. + * + * GCDWebServer instances can be created and used from any thread but it's + * recommended to have the main thread's runloop be running so internal callbacks + * can be handled e.g. for Bonjour registration. + * + * See the README.md file for more information about the architecture of GCDWebServer. + */ +@interface GCDWebServer : NSObject + +/** + * Sets the delegate for the server. + */ +@property(nonatomic, weak, nullable) id delegate; + +/** + * Returns YES if the server is currently running. + */ +@property(nonatomic, readonly, getter=isRunning) BOOL running; + +/** + * Returns the port used by the server. + * + * @warning This property is only valid if the server is running. + */ +@property(nonatomic, readonly) NSUInteger port; + +/** + * Returns the Bonjour name used by the server. + * + * @warning This property is only valid if the server is running and Bonjour + * registration has successfully completed, which can take up to a few seconds. + */ +@property(nonatomic, readonly, nullable) NSString* bonjourName; + +/** + * Returns the Bonjour service type used by the server. + * + * @warning This property is only valid if the server is running and Bonjour + * registration has successfully completed, which can take up to a few seconds. + */ +@property(nonatomic, readonly, nullable) NSString* bonjourType; + +/** + * This method is the designated initializer for the class. + */ +- (instancetype)init; + +/** + * Adds to the server a handler that generates responses synchronously when handling incoming HTTP requests. + * + * Handlers are called in a LIFO queue, so if multiple handlers can potentially + * respond to a given request, the latest added one wins. + * + * @warning Addling handlers while the server is running is not allowed. + */ +- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock; + +/** + * Adds to the server a handler that generates responses asynchronously when handling incoming HTTP requests. + * + * Handlers are called in a LIFO queue, so if multiple handlers can potentially + * respond to a given request, the latest added one wins. + * + * @warning Addling handlers while the server is running is not allowed. + */ +- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock; + +/** + * Removes all handlers previously added to the server. + * + * @warning Removing handlers while the server is running is not allowed. + */ +- (void)removeAllHandlers; + +/** + * Starts the server with explicit options. This method is the designated way + * to start the server. + * + * Returns NO if the server failed to start and sets "error" argument if not NULL. + */ +- (BOOL)startWithOptions:(nullable NSDictionary*)options error:(NSError** _Nullable)error; + +/** + * Stops the server and prevents it to accepts new HTTP requests. + * + * @warning Stopping the server does not abort GCDWebServerConnection instances + * currently handling already received HTTP requests. These connections will + * continue to execute normally until completion. + */ +- (void)stop; + +@end + +@interface GCDWebServer (Extensions) + +/** + * Returns the server's URL. + * + * @warning This property is only valid if the server is running. + */ +@property(nonatomic, readonly, nullable) NSURL* serverURL; + +/** + * Returns the server's Bonjour URL. + * + * @warning This property is only valid if the server is running and Bonjour + * registration has successfully completed, which can take up to a few seconds. + * Also be aware this property will not automatically update if the Bonjour hostname + * has been dynamically changed after the server started running (this should be rare). + */ +@property(nonatomic, readonly, nullable) NSURL* bonjourServerURL; + +/** + * Returns the server's public URL. + * + * @warning This property is only valid if the server is running and NAT port + * mapping is active. + */ +@property(nonatomic, readonly, nullable) NSURL* publicServerURL; + +/** + * Starts the server on port 8080 (OS X & iOS Simulator) or port 80 (iOS) + * using the default Bonjour name. + * + * Returns NO if the server failed to start. + */ +- (BOOL)start; + +/** + * Starts the server on a given port and with a specific Bonjour name. + * Pass a nil Bonjour name to disable Bonjour entirely or an empty string to + * use the default name. + * + * Returns NO if the server failed to start. + */ +- (BOOL)startWithPort:(NSUInteger)port bonjourName:(nullable NSString*)name; + +#if !TARGET_OS_IPHONE + +/** + * Runs the server synchronously using -startWithPort:bonjourName: until a + * SIGINT signal is received i.e. Ctrl-C. This method is intended to be used + * by command line tools. + * + * Returns NO if the server failed to start. + * + * @warning This method must be used from the main thread only. + */ +- (BOOL)runWithPort:(NSUInteger)port bonjourName:(nullable NSString*)name; + +/** + * Runs the server synchronously using -startWithOptions: until a SIGTERM or + * SIGINT signal is received i.e. Ctrl-C in Terminal. This method is intended to + * be used by command line tools. + * + * Returns NO if the server failed to start and sets "error" argument if not NULL. + * + * @warning This method must be used from the main thread only. + */ +- (BOOL)runWithOptions:(nullable NSDictionary*)options error:(NSError** _Nullable)error; + +#endif + +@end + +@interface GCDWebServer (Handlers) + +/** + * Adds a default handler to the server to handle all incoming HTTP requests + * with a given HTTP method and generate responses synchronously. + */ +- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block; + +/** + * Adds a default handler to the server to handle all incoming HTTP requests + * with a given HTTP method and generate responses asynchronously. + */ +- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block; + +/** + * Adds a handler to the server to handle incoming HTTP requests with a given + * HTTP method and a specific case-insensitive path and generate responses + * synchronously. + */ +- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block; + +/** + * Adds a handler to the server to handle incoming HTTP requests with a given + * HTTP method and a specific case-insensitive path and generate responses + * asynchronously. + */ +- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block; + +/** + * Adds a handler to the server to handle incoming HTTP requests with a given + * HTTP method and a path matching a case-insensitive regular expression and + * generate responses synchronously. + */ +- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block; + +/** + * Adds a handler to the server to handle incoming HTTP requests with a given + * HTTP method and a path matching a case-insensitive regular expression and + * generate responses asynchronously. + */ +- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block; + +@end + +@interface GCDWebServer (GETHandlers) + +/** + * Adds a handler to the server to respond to incoming "GET" HTTP requests + * with a specific case-insensitive path with in-memory data. + */ +- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(nullable NSString*)contentType cacheAge:(NSUInteger)cacheAge; + +/** + * Adds a handler to the server to respond to incoming "GET" HTTP requests + * with a specific case-insensitive path with a file. + */ +- (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests; + +/** + * Adds a handler to the server to respond to incoming "GET" HTTP requests + * with a case-insensitive path inside a base path with the corresponding file + * inside a local directory. If no local file matches the request path, a 401 + * HTTP status code is returned to the client. + * + * The "indexFilename" argument allows to specify an "index" file name to use + * when the request path corresponds to a directory. + */ +- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(nullable NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests; + +@end + +/** + * GCDWebServer provides its own built-in logging facility which is used by + * default. It simply sends log messages to stderr assuming it is connected + * to a terminal type device. + * + * GCDWebServer is also compatible with a limited set of third-party logging + * facilities. If one of them is available at compile time, GCDWebServer will + * automatically use it in place of the built-in one. + * + * Currently supported third-party logging facilities are: + * - XLFacility (by the same author as GCDWebServer): https://github.com/swisspol/XLFacility + * + * For the built-in logging facility, the default logging level is INFO + * (or DEBUG if the preprocessor constant "DEBUG" evaluates to non-zero at + * compile time). + * + * It's possible to have GCDWebServer use a custom logging facility by defining + * the "__GCDWEBSERVER_LOGGING_HEADER__" preprocessor constant in Xcode build + * settings to the name of a custom header file (escaped like \"MyLogging.h\"). + * This header file must define the following set of macros: + * + * GWS_LOG_DEBUG(...) + * GWS_LOG_VERBOSE(...) + * GWS_LOG_INFO(...) + * GWS_LOG_WARNING(...) + * GWS_LOG_ERROR(...) + * + * IMPORTANT: These macros must behave like NSLog(). Furthermore the GWS_LOG_DEBUG() + * macro should not do anything unless the preprocessor constant "DEBUG" evaluates + * to non-zero. + * + * The logging methods below send log messages to the same logging facility + * used by GCDWebServer. They can be used for consistency wherever you interact + * with GCDWebServer in your code (e.g. in the implementation of handlers). + */ +@interface GCDWebServer (Logging) + +/** + * Sets the log level of the logging facility below which log messages are discarded. + * + * @warning The interpretation of the "level" argument depends on the logging + * facility used at compile time. + * + * If using the built-in logging facility, the log levels are as follow: + * DEBUG = 0 + * VERBOSE = 1 + * INFO = 2 + * WARNING = 3 + * ERROR = 4 + */ ++ (void)setLogLevel:(int)level; + +/** + * Logs a message to the logging facility at the VERBOSE level. + */ +- (void)logVerbose:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2); + +/** + * Logs a message to the logging facility at the INFO level. + */ +- (void)logInfo:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2); + +/** + * Logs a message to the logging facility at the WARNING level. + */ +- (void)logWarning:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2); + +/** + * Logs a message to the logging facility at the ERROR level. + */ +- (void)logError:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2); + +@end + +#ifdef __GCDWEBSERVER_ENABLE_TESTING__ + +@interface GCDWebServer (Testing) + +/** + * Activates recording of HTTP requests and responses which create files in the + * current directory containing the raw data for all requests and responses. + * + * @warning The current directory must not contain any prior recording files. + */ +@property(nonatomic, getter=isRecordingEnabled) BOOL recordingEnabled; + +/** + * Runs tests by playing back pre-recorded HTTP requests in the given directory + * and comparing the generated responses with the pre-recorded ones. + * + * Returns the number of failed tests or -1 if server failed to start. + */ +- (NSInteger)runTestsWithOptions:(nullable NSDictionary*)options inDirectory:(NSString*)path; + +@end + +#endif + +NS_ASSUME_NONNULL_END diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServer.m b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServer.m new file mode 100644 index 0000000..e52ccd4 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServer.m @@ -0,0 +1,1312 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error GCDWebServer requires ARC +#endif + +#import +#if TARGET_OS_IPHONE +#import +#else +#ifdef __GCDWEBSERVER_ENABLE_TESTING__ +#import +#endif +#endif +#import +#import + +#import "GCDWebServerPrivate.h" + +#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR +#define kDefaultPort 80 +#else +#define kDefaultPort 8080 +#endif + +#define kBonjourResolutionTimeout 5.0 + +NSString* const GCDWebServerOption_Port = @"Port"; +NSString* const GCDWebServerOption_BonjourName = @"BonjourName"; +NSString* const GCDWebServerOption_BonjourType = @"BonjourType"; +NSString* const GCDWebServerOption_RequestNATPortMapping = @"RequestNATPortMapping"; +NSString* const GCDWebServerOption_BindToLocalhost = @"BindToLocalhost"; +NSString* const GCDWebServerOption_MaxPendingConnections = @"MaxPendingConnections"; +NSString* const GCDWebServerOption_ServerName = @"ServerName"; +NSString* const GCDWebServerOption_AuthenticationMethod = @"AuthenticationMethod"; +NSString* const GCDWebServerOption_AuthenticationRealm = @"AuthenticationRealm"; +NSString* const GCDWebServerOption_AuthenticationAccounts = @"AuthenticationAccounts"; +NSString* const GCDWebServerOption_ConnectionClass = @"ConnectionClass"; +NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET = @"AutomaticallyMapHEADToGET"; +NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval = @"ConnectedStateCoalescingInterval"; +NSString* const GCDWebServerOption_DispatchQueuePriority = @"DispatchQueuePriority"; +#if TARGET_OS_IPHONE +NSString* const GCDWebServerOption_AutomaticallySuspendInBackground = @"AutomaticallySuspendInBackground"; +#endif + +NSString* const GCDWebServerAuthenticationMethod_Basic = @"Basic"; +NSString* const GCDWebServerAuthenticationMethod_DigestAccess = @"DigestAccess"; + +#if defined(__GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__) +#if DEBUG +GCDWebServerLoggingLevel GCDWebServerLogLevel = kGCDWebServerLoggingLevel_Debug; +#else +GCDWebServerLoggingLevel GCDWebServerLogLevel = kGCDWebServerLoggingLevel_Info; +#endif +#endif + +#if !TARGET_OS_IPHONE +static BOOL _run; +#endif + +#ifdef __GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__ + +void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ...) { + static const char* levelNames[] = {"DEBUG", "VERBOSE", "INFO", "WARNING", "ERROR"}; + static int enableLogging = -1; + if (enableLogging < 0) { + enableLogging = (isatty(STDERR_FILENO) ? 1 : 0); + } + if (enableLogging) { + va_list arguments; + va_start(arguments, format); + NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments]; + va_end(arguments); + fprintf(stderr, "[%s] %s\n", levelNames[level], [message UTF8String]); + } +} + +#endif + +#if !TARGET_OS_IPHONE + +static void _SignalHandler(int signal) { + _run = NO; + printf("\n"); +} + +#endif + +#if !TARGET_OS_IPHONE || defined(__GCDWEBSERVER_ENABLE_TESTING__) + +// This utility function is used to ensure scheduled callbacks on the main thread are called when running the server synchronously +// https://developer.apple.com/library/mac/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html +// The main queue works with the application’s run loop to interleave the execution of queued tasks with the execution of other event sources attached to the run loop +// TODO: Ensure all scheduled blocks on the main queue are also executed +static void _ExecuteMainThreadRunLoopSources() { + SInt32 result; + do { + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true); + } while (result == kCFRunLoopRunHandledSource); +} + +#endif + +@implementation GCDWebServerHandler + +- (instancetype)initWithMatchBlock:(GCDWebServerMatchBlock _Nonnull)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock _Nonnull)processBlock { + if ((self = [super init])) { + _matchBlock = [matchBlock copy]; + _asyncProcessBlock = [processBlock copy]; + } + return self; +} + +@end + +@implementation GCDWebServer { + dispatch_queue_t _syncQueue; + dispatch_group_t _sourceGroup; + NSMutableArray* _handlers; + NSInteger _activeConnections; // Accessed through _syncQueue only + BOOL _connected; // Accessed on main thread only + CFRunLoopTimerRef _disconnectTimer; // Accessed on main thread only + + NSDictionary* _options; + NSMutableDictionary* _authenticationBasicAccounts; + NSMutableDictionary* _authenticationDigestAccounts; + Class _connectionClass; + CFTimeInterval _disconnectDelay; + dispatch_source_t _source4; + dispatch_source_t _source6; + CFNetServiceRef _registrationService; + CFNetServiceRef _resolutionService; + DNSServiceRef _dnsService; + CFSocketRef _dnsSocket; + CFRunLoopSourceRef _dnsSource; + NSString* _dnsAddress; + NSUInteger _dnsPort; + BOOL _bindToLocalhost; +#if TARGET_OS_IPHONE + BOOL _suspendInBackground; + UIBackgroundTaskIdentifier _backgroundTask; +#endif +#ifdef __GCDWEBSERVER_ENABLE_TESTING__ + BOOL _recording; +#endif +} + ++ (void)initialize { + GCDWebServerInitializeFunctions(); +} + +- (instancetype)init { + if ((self = [super init])) { + _syncQueue = dispatch_queue_create([NSStringFromClass([self class]) UTF8String], DISPATCH_QUEUE_SERIAL); + _sourceGroup = dispatch_group_create(); + _handlers = [[NSMutableArray alloc] init]; +#if TARGET_OS_IPHONE + _backgroundTask = UIBackgroundTaskInvalid; +#endif + } + return self; +} + +- (void)dealloc { + GWS_DCHECK(_connected == NO); + GWS_DCHECK(_activeConnections == 0); + GWS_DCHECK(_options == nil); // The server can never be dealloc'ed while running because of the retain-cycle with the dispatch source + GWS_DCHECK(_disconnectTimer == NULL); // The server can never be dealloc'ed while the disconnect timer is pending because of the retain-cycle + +#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE + dispatch_release(_sourceGroup); + dispatch_release(_syncQueue); +#endif +} + +#if TARGET_OS_IPHONE + +// Always called on main thread +- (void)_startBackgroundTask { + GWS_DCHECK([NSThread isMainThread]); + if (_backgroundTask == UIBackgroundTaskInvalid) { + GWS_LOG_DEBUG(@"Did start background task"); + _backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ + + GWS_LOG_WARNING(@"Application is being suspended while %@ is still connected", [self class]); + [self _endBackgroundTask]; + + }]; + } else { + GWS_DNOT_REACHED(); + } +} + +#endif + +// Always called on main thread +- (void)_didConnect { + GWS_DCHECK([NSThread isMainThread]); + GWS_DCHECK(_connected == NO); + _connected = YES; + GWS_LOG_DEBUG(@"Did connect"); + +#if TARGET_OS_IPHONE + if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) { + [self _startBackgroundTask]; + } +#endif + + if ([_delegate respondsToSelector:@selector(webServerDidConnect:)]) { + [_delegate webServerDidConnect:self]; + } +} + +- (void)willStartConnection:(GCDWebServerConnection*)connection { + dispatch_sync(_syncQueue, ^{ + + GWS_DCHECK(_activeConnections >= 0); + if (_activeConnections == 0) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (_disconnectTimer) { + CFRunLoopTimerInvalidate(_disconnectTimer); + CFRelease(_disconnectTimer); + _disconnectTimer = NULL; + } + if (_connected == NO) { + [self _didConnect]; + } + }); + } + _activeConnections += 1; + + }); +} + +#if TARGET_OS_IPHONE + +// Always called on main thread +- (void)_endBackgroundTask { + GWS_DCHECK([NSThread isMainThread]); + if (_backgroundTask != UIBackgroundTaskInvalid) { + if (_suspendInBackground && ([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground) && _source4) { + [self _stop]; + } + [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask]; + _backgroundTask = UIBackgroundTaskInvalid; + GWS_LOG_DEBUG(@"Did end background task"); + } +} + +#endif + +// Always called on main thread +- (void)_didDisconnect { + GWS_DCHECK([NSThread isMainThread]); + GWS_DCHECK(_connected == YES); + _connected = NO; + GWS_LOG_DEBUG(@"Did disconnect"); + +#if TARGET_OS_IPHONE + [self _endBackgroundTask]; +#endif + + if ([_delegate respondsToSelector:@selector(webServerDidDisconnect:)]) { + [_delegate webServerDidDisconnect:self]; + } +} + +- (void)didEndConnection:(GCDWebServerConnection*)connection { + dispatch_sync(_syncQueue, ^{ + GWS_DCHECK(_activeConnections > 0); + _activeConnections -= 1; + if (_activeConnections == 0) { + dispatch_async(dispatch_get_main_queue(), ^{ + if ((_disconnectDelay > 0.0) && (_source4 != NULL)) { + if (_disconnectTimer) { + CFRunLoopTimerInvalidate(_disconnectTimer); + CFRelease(_disconnectTimer); + } + _disconnectTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + _disconnectDelay, 0.0, 0, 0, ^(CFRunLoopTimerRef timer) { + GWS_DCHECK([NSThread isMainThread]); + [self _didDisconnect]; + CFRelease(_disconnectTimer); + _disconnectTimer = NULL; + }); + CFRunLoopAddTimer(CFRunLoopGetMain(), _disconnectTimer, kCFRunLoopCommonModes); + } else { + [self _didDisconnect]; + } + }); + } + }); +} + +- (NSString*)bonjourName { + CFStringRef name = _resolutionService ? CFNetServiceGetName(_resolutionService) : NULL; + return name && CFStringGetLength(name) ? CFBridgingRelease(CFStringCreateCopy(kCFAllocatorDefault, name)) : nil; +} + +- (NSString*)bonjourType { + CFStringRef type = _resolutionService ? CFNetServiceGetType(_resolutionService) : NULL; + return type && CFStringGetLength(type) ? CFBridgingRelease(CFStringCreateCopy(kCFAllocatorDefault, type)) : nil; +} + +- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock { + [self addHandlerWithMatchBlock:matchBlock + asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { + completionBlock(processBlock(request)); + }]; +} + +- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock { + GWS_DCHECK(_options == nil); + GCDWebServerHandler* handler = [[GCDWebServerHandler alloc] initWithMatchBlock:matchBlock asyncProcessBlock:processBlock]; + [_handlers insertObject:handler atIndex:0]; +} + +- (void)removeAllHandlers { + GWS_DCHECK(_options == nil); + [_handlers removeAllObjects]; +} + +static void _NetServiceRegisterCallBack(CFNetServiceRef service, CFStreamError* error, void* info) { + GWS_DCHECK([NSThread isMainThread]); + @autoreleasepool { + if (error->error) { + GWS_LOG_ERROR(@"Bonjour registration error %i (domain %i)", (int)error->error, (int)error->domain); + } else { + GCDWebServer* server = (__bridge GCDWebServer*)info; + GWS_LOG_VERBOSE(@"Bonjour registration complete for %@", [server class]); + if (!CFNetServiceResolveWithTimeout(server->_resolutionService, kBonjourResolutionTimeout, NULL)) { + GWS_LOG_ERROR(@"Failed starting Bonjour resolution"); + GWS_DNOT_REACHED(); + } + } + } +} + +static void _NetServiceResolveCallBack(CFNetServiceRef service, CFStreamError* error, void* info) { + GWS_DCHECK([NSThread isMainThread]); + @autoreleasepool { + if (error->error) { + if ((error->domain != kCFStreamErrorDomainNetServices) && (error->error != kCFNetServicesErrorTimeout)) { + GWS_LOG_ERROR(@"Bonjour resolution error %i (domain %i)", (int)error->error, (int)error->domain); + } + } else { + GCDWebServer* server = (__bridge GCDWebServer*)info; + GWS_LOG_INFO(@"%@ now locally reachable at %@", [server class], server.bonjourServerURL); + if ([server.delegate respondsToSelector:@selector(webServerDidCompleteBonjourRegistration:)]) { + [server.delegate webServerDidCompleteBonjourRegistration:server]; + } + } + } +} + +static void _DNSServiceCallBack(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, uint32_t externalAddress, DNSServiceProtocol protocol, uint16_t internalPort, uint16_t externalPort, uint32_t ttl, void* context) { + GWS_DCHECK([NSThread isMainThread]); + @autoreleasepool { + GCDWebServer* server = (__bridge GCDWebServer*)context; + if ((errorCode == kDNSServiceErr_NoError) || (errorCode == kDNSServiceErr_DoubleNAT)) { + struct sockaddr_in addr4; + bzero(&addr4, sizeof(addr4)); + addr4.sin_len = sizeof(addr4); + addr4.sin_family = AF_INET; + addr4.sin_addr.s_addr = externalAddress; // Already in network byte order + server->_dnsAddress = GCDWebServerStringFromSockAddr((const struct sockaddr*)&addr4, NO); + server->_dnsPort = ntohs(externalPort); + GWS_LOG_INFO(@"%@ now publicly reachable at %@", [server class], server.publicServerURL); + } else { + GWS_LOG_ERROR(@"DNS service error %i", errorCode); + server->_dnsAddress = nil; + server->_dnsPort = 0; + } + if ([server.delegate respondsToSelector:@selector(webServerDidUpdateNATPortMapping:)]) { + [server.delegate webServerDidUpdateNATPortMapping:server]; + } + } +} + +static void _SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info) { + GWS_DCHECK([NSThread isMainThread]); + @autoreleasepool { + GCDWebServer* server = (__bridge GCDWebServer*)info; + DNSServiceErrorType status = DNSServiceProcessResult(server->_dnsService); + if (status != kDNSServiceErr_NoError) { + GWS_LOG_ERROR(@"DNS service error %i", status); + } + } +} + +static inline id _GetOption(NSDictionary* options, NSString* key, id defaultValue) { + id value = [options objectForKey:key]; + return value ? value : defaultValue; +} + +static inline NSString* _EncodeBase64(NSString* string) { + NSData* data = [string dataUsingEncoding:NSUTF8StringEncoding]; +#if (TARGET_OS_IPHONE && !(__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0)) || (!TARGET_OS_IPHONE && !(__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_9)) + if (![data respondsToSelector:@selector(base64EncodedDataWithOptions:)]) { + return [data base64Encoding]; + } +#endif + return [[NSString alloc] initWithData:[data base64EncodedDataWithOptions:0] encoding:NSASCIIStringEncoding]; +} + +- (int)_createListeningSocket:(BOOL)useIPv6 + localAddress:(const void*)address + length:(socklen_t)length + maxPendingConnections:(NSUInteger)maxPendingConnections + error:(NSError**)error { + int listeningSocket = socket(useIPv6 ? PF_INET6 : PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listeningSocket > 0) { + int yes = 1; + setsockopt(listeningSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + + if (bind(listeningSocket, address, length) == 0) { + if (listen(listeningSocket, (int)maxPendingConnections) == 0) { + GWS_LOG_DEBUG(@"Did open %s listening socket %i", useIPv6 ? "IPv6" : "IPv4", listeningSocket); + return listeningSocket; + } else { + if (error) { + *error = GCDWebServerMakePosixError(errno); + } + GWS_LOG_ERROR(@"Failed starting %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno); + close(listeningSocket); + } + } else { + if (error) { + *error = GCDWebServerMakePosixError(errno); + } + GWS_LOG_ERROR(@"Failed binding %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno); + close(listeningSocket); + } + + } else { + if (error) { + *error = GCDWebServerMakePosixError(errno); + } + GWS_LOG_ERROR(@"Failed creating %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno); + } + return -1; +} + +- (dispatch_source_t)_createDispatchSourceWithListeningSocket:(int)listeningSocket isIPv6:(BOOL)isIPv6 { + dispatch_group_enter(_sourceGroup); + dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, dispatch_get_global_queue(_dispatchQueuePriority, 0)); + dispatch_source_set_cancel_handler(source, ^{ + + @autoreleasepool { + int result = close(listeningSocket); + if (result != 0) { + GWS_LOG_ERROR(@"Failed closing %s listening socket: %s (%i)", isIPv6 ? "IPv6" : "IPv4", strerror(errno), errno); + } else { + GWS_LOG_DEBUG(@"Did close %s listening socket %i", isIPv6 ? "IPv6" : "IPv4", listeningSocket); + } + } + dispatch_group_leave(_sourceGroup); + + }); + dispatch_source_set_event_handler(source, ^{ + + @autoreleasepool { + struct sockaddr_storage remoteSockAddr; + socklen_t remoteAddrLen = sizeof(remoteSockAddr); + int socket = accept(listeningSocket, (struct sockaddr*)&remoteSockAddr, &remoteAddrLen); + if (socket > 0) { + NSData* remoteAddress = [NSData dataWithBytes:&remoteSockAddr length:remoteAddrLen]; + + struct sockaddr_storage localSockAddr; + socklen_t localAddrLen = sizeof(localSockAddr); + NSData* localAddress = nil; + if (getsockname(socket, (struct sockaddr*)&localSockAddr, &localAddrLen) == 0) { + localAddress = [NSData dataWithBytes:&localSockAddr length:localAddrLen]; + GWS_DCHECK((!isIPv6 && localSockAddr.ss_family == AF_INET) || (isIPv6 && localSockAddr.ss_family == AF_INET6)); + } else { + GWS_DNOT_REACHED(); + } + + int noSigPipe = 1; + setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)); // Make sure this socket cannot generate SIG_PIPE + + GCDWebServerConnection* connection = [[_connectionClass alloc] initWithServer:self localAddress:localAddress remoteAddress:remoteAddress socket:socket]; // Connection will automatically retain itself while opened + [connection self]; // Prevent compiler from complaining about unused variable / useless statement + } else { + GWS_LOG_ERROR(@"Failed accepting %s socket: %s (%i)", isIPv6 ? "IPv6" : "IPv4", strerror(errno), errno); + } + } + + }); + return source; +} + +- (BOOL)_start:(NSError**)error { + GWS_DCHECK(_source4 == NULL); + + NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue]; + BOOL bindToLocalhost = [_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue]; + NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue]; + + struct sockaddr_in addr4; + bzero(&addr4, sizeof(addr4)); + addr4.sin_len = sizeof(addr4); + addr4.sin_family = AF_INET; + addr4.sin_port = htons(port); + addr4.sin_addr.s_addr = bindToLocalhost ? htonl(INADDR_LOOPBACK) : htonl(INADDR_ANY); + int listeningSocket4 = [self _createListeningSocket:NO localAddress:&addr4 length:sizeof(addr4) maxPendingConnections:maxPendingConnections error:error]; + if (listeningSocket4 <= 0) { + return NO; + } + if (port == 0) { + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + if (getsockname(listeningSocket4, (struct sockaddr*)&addr, &addrlen) == 0) { + port = ntohs(addr.sin_port); + } else { + GWS_LOG_ERROR(@"Failed retrieving socket address: %s (%i)", strerror(errno), errno); + } + } + + struct sockaddr_in6 addr6; + bzero(&addr6, sizeof(addr6)); + addr6.sin6_len = sizeof(addr6); + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(port); + addr6.sin6_addr = bindToLocalhost ? in6addr_loopback : in6addr_any; + int listeningSocket6 = [self _createListeningSocket:YES localAddress:&addr6 length:sizeof(addr6) maxPendingConnections:maxPendingConnections error:error]; + if (listeningSocket6 <= 0) { + close(listeningSocket4); + return NO; + } + + _serverName = [_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy]; + NSString* authenticationMethod = _GetOption(_options, GCDWebServerOption_AuthenticationMethod, nil); + if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_Basic]) { + _authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy]; + _authenticationBasicAccounts = [[NSMutableDictionary alloc] init]; + NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{}); + [accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) { + [_authenticationBasicAccounts setObject:_EncodeBase64([NSString stringWithFormat:@"%@:%@", username, password]) forKey:username]; + }]; + } else if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_DigestAccess]) { + _authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy]; + _authenticationDigestAccounts = [[NSMutableDictionary alloc] init]; + NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{}); + [accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) { + [_authenticationDigestAccounts setObject:GCDWebServerComputeMD5Digest(@"%@:%@:%@", username, _authenticationRealm, password) forKey:username]; + }]; + } + _connectionClass = _GetOption(_options, GCDWebServerOption_ConnectionClass, [GCDWebServerConnection class]); + _shouldAutomaticallyMapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue]; + _disconnectDelay = [_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue]; + _dispatchQueuePriority = [_GetOption(_options, GCDWebServerOption_DispatchQueuePriority, @(DISPATCH_QUEUE_PRIORITY_DEFAULT)) longValue]; + + _source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO]; + _source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES]; + _port = port; + _bindToLocalhost = bindToLocalhost; + + NSString* bonjourName = _GetOption(_options, GCDWebServerOption_BonjourName, nil); + NSString* bonjourType = _GetOption(_options, GCDWebServerOption_BonjourType, @"_http._tcp"); + if (bonjourName) { + _registrationService = CFNetServiceCreate(kCFAllocatorDefault, CFSTR("local."), (__bridge CFStringRef)bonjourType, (__bridge CFStringRef)(bonjourName.length ? bonjourName : _serverName), (SInt32)_port); + if (_registrationService) { + CFNetServiceClientContext context = {0, (__bridge void*)self, NULL, NULL, NULL}; + + CFNetServiceSetClient(_registrationService, _NetServiceRegisterCallBack, &context); + CFNetServiceScheduleWithRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes); + CFStreamError streamError = {0}; + CFNetServiceRegisterWithOptions(_registrationService, 0, &streamError); + + _resolutionService = CFNetServiceCreateCopy(kCFAllocatorDefault, _registrationService); + if (_resolutionService) { + CFNetServiceSetClient(_resolutionService, _NetServiceResolveCallBack, &context); + CFNetServiceScheduleWithRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes); + } else { + GWS_LOG_ERROR(@"Failed creating CFNetService for resolution"); + } + } else { + GWS_LOG_ERROR(@"Failed creating CFNetService for registration"); + } + } + + if ([_GetOption(_options, GCDWebServerOption_RequestNATPortMapping, @NO) boolValue]) { + DNSServiceErrorType status = DNSServiceNATPortMappingCreate(&_dnsService, 0, 0, kDNSServiceProtocol_TCP, htons(port), htons(port), 0, _DNSServiceCallBack, (__bridge void*)self); + if (status == kDNSServiceErr_NoError) { + CFSocketContext context = {0, (__bridge void*)self, NULL, NULL, NULL}; + _dnsSocket = CFSocketCreateWithNative(kCFAllocatorDefault, DNSServiceRefSockFD(_dnsService), kCFSocketReadCallBack, _SocketCallBack, &context); + if (_dnsSocket) { + CFSocketSetSocketFlags(_dnsSocket, CFSocketGetSocketFlags(_dnsSocket) & ~kCFSocketCloseOnInvalidate); + _dnsSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _dnsSocket, 0); + if (_dnsSource) { + CFRunLoopAddSource(CFRunLoopGetMain(), _dnsSource, kCFRunLoopCommonModes); + } else { + GWS_LOG_ERROR(@"Failed creating CFRunLoopSource"); + GWS_DNOT_REACHED(); + } + } else { + GWS_LOG_ERROR(@"Failed creating CFSocket"); + GWS_DNOT_REACHED(); + } + } else { + GWS_LOG_ERROR(@"Failed creating NAT port mapping (%i)", status); + } + } + + dispatch_resume(_source4); + dispatch_resume(_source6); + GWS_LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL); + if ([_delegate respondsToSelector:@selector(webServerDidStart:)]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [_delegate webServerDidStart:self]; + }); + } + + return YES; +} + +- (void)_stop { + GWS_DCHECK(_source4 != NULL); + + if (_dnsService) { + _dnsAddress = nil; + _dnsPort = 0; + if (_dnsSource) { + CFRunLoopSourceInvalidate(_dnsSource); + CFRelease(_dnsSource); + _dnsSource = NULL; + } + if (_dnsSocket) { + CFRelease(_dnsSocket); + _dnsSocket = NULL; + } + DNSServiceRefDeallocate(_dnsService); + _dnsService = NULL; + } + + if (_registrationService) { + if (_resolutionService) { + CFNetServiceUnscheduleFromRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes); + CFNetServiceSetClient(_resolutionService, NULL, NULL); + CFNetServiceCancel(_resolutionService); + CFRelease(_resolutionService); + _resolutionService = NULL; + } + CFNetServiceUnscheduleFromRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes); + CFNetServiceSetClient(_registrationService, NULL, NULL); + CFNetServiceCancel(_registrationService); + CFRelease(_registrationService); + _registrationService = NULL; + } + + dispatch_source_cancel(_source6); + dispatch_source_cancel(_source4); + dispatch_group_wait(_sourceGroup, DISPATCH_TIME_FOREVER); // Wait until the cancellation handlers have been called which guarantees the listening sockets are closed +#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE + dispatch_release(_source6); +#endif + _source6 = NULL; +#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE + dispatch_release(_source4); +#endif + _source4 = NULL; + _port = 0; + _bindToLocalhost = NO; + + _serverName = nil; + _authenticationRealm = nil; + _authenticationBasicAccounts = nil; + _authenticationDigestAccounts = nil; + + dispatch_async(dispatch_get_main_queue(), ^{ + if (_disconnectTimer) { + CFRunLoopTimerInvalidate(_disconnectTimer); + CFRelease(_disconnectTimer); + _disconnectTimer = NULL; + [self _didDisconnect]; + } + }); + + GWS_LOG_INFO(@"%@ stopped", [self class]); + if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [_delegate webServerDidStop:self]; + }); + } +} + +#if TARGET_OS_IPHONE + +- (void)_didEnterBackground:(NSNotification*)notification { + GWS_DCHECK([NSThread isMainThread]); + GWS_LOG_DEBUG(@"Did enter background"); + if ((_backgroundTask == UIBackgroundTaskInvalid) && _source4) { + [self _stop]; + } +} + +- (void)_willEnterForeground:(NSNotification*)notification { + GWS_DCHECK([NSThread isMainThread]); + GWS_LOG_DEBUG(@"Will enter foreground"); + if (!_source4) { + [self _start:NULL]; // TODO: There's probably nothing we can do on failure + } +} + +#endif + +- (BOOL)startWithOptions:(NSDictionary*)options error:(NSError**)error { + if (_options == nil) { + _options = options ? [options copy] : @{}; +#if TARGET_OS_IPHONE + _suspendInBackground = [_GetOption(_options, GCDWebServerOption_AutomaticallySuspendInBackground, @YES) boolValue]; + if (((_suspendInBackground == NO) || ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground)) && ![self _start:error]) +#else + if (![self _start:error]) +#endif + { + _options = nil; + return NO; + } +#if TARGET_OS_IPHONE + if (_suspendInBackground) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; + } +#endif + return YES; + } else { + GWS_DNOT_REACHED(); + } + return NO; +} + +- (BOOL)isRunning { + return (_options ? YES : NO); +} + +- (void)stop { + if (_options) { +#if TARGET_OS_IPHONE + if (_suspendInBackground) { + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]; + } +#endif + if (_source4) { + [self _stop]; + } + _options = nil; + } else { + GWS_DNOT_REACHED(); + } +} + +@end + +@implementation GCDWebServer (Extensions) + +- (NSURL*)serverURL { + if (_source4) { + NSString* ipAddress = _bindToLocalhost ? @"localhost" : GCDWebServerGetPrimaryIPAddress(NO); // We can't really use IPv6 anyway as it doesn't work great with HTTP URLs in practice + if (ipAddress) { + if (_port != 80) { + return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%i/", ipAddress, (int)_port]]; + } else { + return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/", ipAddress]]; + } + } + } + return nil; +} + +- (NSURL*)bonjourServerURL { + if (_source4 && _resolutionService) { + NSString* name = (__bridge NSString*)CFNetServiceGetTargetHost(_resolutionService); + if (name.length) { + name = [name substringToIndex:(name.length - 1)]; // Strip trailing period at end of domain + if (_port != 80) { + return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%i/", name, (int)_port]]; + } else { + return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/", name]]; + } + } + } + return nil; +} + +- (NSURL*)publicServerURL { + if (_source4 && _dnsService && _dnsAddress && _dnsPort) { + if (_dnsPort != 80) { + return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%i/", _dnsAddress, (int)_dnsPort]]; + } else { + return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/", _dnsAddress]]; + } + } + return nil; +} + +- (BOOL)start { + return [self startWithPort:kDefaultPort bonjourName:@""]; +} + +- (BOOL)startWithPort:(NSUInteger)port bonjourName:(NSString*)name { + NSMutableDictionary* options = [NSMutableDictionary dictionary]; + [options setObject:[NSNumber numberWithInteger:port] forKey:GCDWebServerOption_Port]; + [options setValue:name forKey:GCDWebServerOption_BonjourName]; + return [self startWithOptions:options error:NULL]; +} + +#if !TARGET_OS_IPHONE + +- (BOOL)runWithPort:(NSUInteger)port bonjourName:(NSString*)name { + NSMutableDictionary* options = [NSMutableDictionary dictionary]; + [options setObject:[NSNumber numberWithInteger:port] forKey:GCDWebServerOption_Port]; + [options setValue:name forKey:GCDWebServerOption_BonjourName]; + return [self runWithOptions:options error:NULL]; +} + +- (BOOL)runWithOptions:(NSDictionary*)options error:(NSError**)error { + GWS_DCHECK([NSThread isMainThread]); + BOOL success = NO; + _run = YES; + void (*termHandler)(int) = signal(SIGTERM, _SignalHandler); + void (*intHandler)(int) = signal(SIGINT, _SignalHandler); + if ((termHandler != SIG_ERR) && (intHandler != SIG_ERR)) { + if ([self startWithOptions:options error:error]) { + while (_run) { + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0, true); + } + [self stop]; + success = YES; + } + _ExecuteMainThreadRunLoopSources(); + signal(SIGINT, intHandler); + signal(SIGTERM, termHandler); + } + return success; +} + +#endif + +@end + +@implementation GCDWebServer (Handlers) + +- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block { + [self addDefaultHandlerForMethod:method + requestClass:aClass + asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { + completionBlock(block(request)); + }]; +} + +- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block { + [self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { + + if (![requestMethod isEqualToString:method]) { + return nil; + } + return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]; + + } + asyncProcessBlock:block]; +} + +- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block { + [self addHandlerForMethod:method + path:path + requestClass:aClass + asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { + completionBlock(block(request)); + }]; +} + +- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block { + if ([path hasPrefix:@"/"] && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) { + [self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { + + if (![requestMethod isEqualToString:method]) { + return nil; + } + if ([urlPath caseInsensitiveCompare:path] != NSOrderedSame) { + return nil; + } + return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]; + + } + asyncProcessBlock:block]; + } else { + GWS_DNOT_REACHED(); + } +} + +- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block { + [self addHandlerForMethod:method + pathRegex:regex + requestClass:aClass + asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { + completionBlock(block(request)); + }]; +} + +- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block { + NSRegularExpression* expression = [NSRegularExpression regularExpressionWithPattern:regex options:NSRegularExpressionCaseInsensitive error:NULL]; + if (expression && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) { + [self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { + + if (![requestMethod isEqualToString:method]) { + return nil; + } + + NSArray* matches = [expression matchesInString:urlPath options:0 range:NSMakeRange(0, urlPath.length)]; + if (matches.count == 0) { + return nil; + } + + NSMutableArray* captures = [NSMutableArray array]; + for (NSTextCheckingResult* result in matches) { + // Start at 1; index 0 is the whole string + for (NSUInteger i = 1; i < result.numberOfRanges; i++) { + NSRange range = [result rangeAtIndex:i]; + // range is {NSNotFound, 0} "if one of the capture groups did not participate in this particular match" + // see discussion in -[NSRegularExpression firstMatchInString:options:range:] + if (range.location != NSNotFound) { + [captures addObject:[urlPath substringWithRange:range]]; + } + } + } + + GCDWebServerRequest* request = [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]; + [request setAttribute:captures forKey:GCDWebServerRequestAttribute_RegexCaptures]; + return request; + + } + asyncProcessBlock:block]; + } else { + GWS_DNOT_REACHED(); + } +} + +@end + +@implementation GCDWebServer (GETHandlers) + +- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(NSString*)contentType cacheAge:(NSUInteger)cacheAge { + [self addHandlerForMethod:@"GET" + path:path + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + + GCDWebServerResponse* response = [GCDWebServerDataResponse responseWithData:staticData contentType:contentType]; + response.cacheControlMaxAge = cacheAge; + return response; + + }]; +} + +- (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests { + [self addHandlerForMethod:@"GET" + path:path + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + + GCDWebServerResponse* response = nil; + if (allowRangeRequests) { + response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange isAttachment:isAttachment]; + [response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"]; + } else { + response = [GCDWebServerFileResponse responseWithFile:filePath isAttachment:isAttachment]; + } + response.cacheControlMaxAge = cacheAge; + return response; + + }]; +} + +- (GCDWebServerResponse*)_responseWithContentsOfDirectory:(NSString*)path { + NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] enumeratorAtPath:path]; + if (enumerator == nil) { + return nil; + } + NSMutableString* html = [NSMutableString string]; + [html appendString:@"\n"]; + [html appendString:@"\n"]; + [html appendString:@"
    \n"]; + for (NSString* file in enumerator) { + if (![file hasPrefix:@"."]) { + NSString* type = [[enumerator fileAttributes] objectForKey:NSFileType]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + NSString* escapedFile = [file stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; +#pragma clang diagnostic pop + GWS_DCHECK(escapedFile); + if ([type isEqualToString:NSFileTypeRegular]) { + [html appendFormat:@"
  • %@
  • \n", escapedFile, file]; + } else if ([type isEqualToString:NSFileTypeDirectory]) { + [html appendFormat:@"
  • %@/
  • \n", escapedFile, file]; + } + } + [enumerator skipDescendents]; + } + [html appendString:@"
\n"]; + [html appendString:@"\n"]; + return [GCDWebServerDataResponse responseWithHTML:html]; +} + +- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests { + if ([basePath hasPrefix:@"/"] && [basePath hasSuffix:@"/"]) { + GCDWebServer* __unsafe_unretained server = self; + [self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) { + + if (![requestMethod isEqualToString:@"GET"]) { + return nil; + } + if (![urlPath hasPrefix:basePath]) { + return nil; + } + return [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]; + + } + processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) { + + GCDWebServerResponse* response = nil; + NSString* filePath = [directoryPath stringByAppendingPathComponent:[request.path substringFromIndex:basePath.length]]; + NSString* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType]; + if (fileType) { + if ([fileType isEqualToString:NSFileTypeDirectory]) { + if (indexFilename) { + NSString* indexPath = [filePath stringByAppendingPathComponent:indexFilename]; + NSString* indexType = [[[NSFileManager defaultManager] attributesOfItemAtPath:indexPath error:NULL] fileType]; + if ([indexType isEqualToString:NSFileTypeRegular]) { + return [GCDWebServerFileResponse responseWithFile:indexPath]; + } + } + response = [server _responseWithContentsOfDirectory:filePath]; + } else if ([fileType isEqualToString:NSFileTypeRegular]) { + if (allowRangeRequests) { + response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange]; + [response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"]; + } else { + response = [GCDWebServerFileResponse responseWithFile:filePath]; + } + } + } + if (response) { + response.cacheControlMaxAge = cacheAge; + } else { + response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound]; + } + return response; + + }]; + } else { + GWS_DNOT_REACHED(); + } +} + +@end + +@implementation GCDWebServer (Logging) + ++ (void)setLogLevel:(int)level { +#if defined(__GCDWEBSERVER_LOGGING_FACILITY_XLFACILITY__) + [XLSharedFacility setMinLogLevel:level]; +#elif defined(__GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__) + GCDWebServerLogLevel = level; +#endif +} + +- (void)logVerbose:(NSString*)format, ... { + va_list arguments; + va_start(arguments, format); + GWS_LOG_VERBOSE(@"%@", [[NSString alloc] initWithFormat:format arguments:arguments]); + va_end(arguments); +} + +- (void)logInfo:(NSString*)format, ... { + va_list arguments; + va_start(arguments, format); + GWS_LOG_INFO(@"%@", [[NSString alloc] initWithFormat:format arguments:arguments]); + va_end(arguments); +} + +- (void)logWarning:(NSString*)format, ... { + va_list arguments; + va_start(arguments, format); + GWS_LOG_WARNING(@"%@", [[NSString alloc] initWithFormat:format arguments:arguments]); + va_end(arguments); +} + +- (void)logError:(NSString*)format, ... { + va_list arguments; + va_start(arguments, format); + GWS_LOG_ERROR(@"%@", [[NSString alloc] initWithFormat:format arguments:arguments]); + va_end(arguments); +} + +@end + +#ifdef __GCDWEBSERVER_ENABLE_TESTING__ + +@implementation GCDWebServer (Testing) + +- (void)setRecordingEnabled:(BOOL)flag { + _recording = flag; +} + +- (BOOL)isRecordingEnabled { + return _recording; +} + +static CFHTTPMessageRef _CreateHTTPMessageFromData(NSData* data, BOOL isRequest) { + CFHTTPMessageRef message = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, isRequest); + if (CFHTTPMessageAppendBytes(message, data.bytes, data.length)) { + return message; + } + CFRelease(message); + return NULL; +} + +static CFHTTPMessageRef _CreateHTTPMessageFromPerformingRequest(NSData* inData, NSUInteger port) { + CFHTTPMessageRef response = NULL; + int httpSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (httpSocket > 0) { + struct sockaddr_in addr4; + bzero(&addr4, sizeof(addr4)); + addr4.sin_len = sizeof(port); + addr4.sin_family = AF_INET; + addr4.sin_port = htons(8080); + addr4.sin_addr.s_addr = htonl(INADDR_ANY); + if (connect(httpSocket, (void*)&addr4, sizeof(addr4)) == 0) { + if (write(httpSocket, inData.bytes, inData.length) == (ssize_t)inData.length) { + NSMutableData* outData = [[NSMutableData alloc] initWithLength:(256 * 1024)]; + NSUInteger length = 0; + while (1) { + ssize_t result = read(httpSocket, (char*)outData.mutableBytes + length, outData.length - length); + if (result < 0) { + length = NSUIntegerMax; + break; + } else if (result == 0) { + break; + } + length += result; + if (length >= outData.length) { + outData.length = 2 * outData.length; + } + } + if (length != NSUIntegerMax) { + outData.length = length; + response = _CreateHTTPMessageFromData(outData, NO); + } else { + GWS_DNOT_REACHED(); + } + } + } + close(httpSocket); + } + return response; +} + +static void _LogResult(NSString* format, ...) { + va_list arguments; + va_start(arguments, format); + NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments]; + va_end(arguments); + fprintf(stdout, "%s\n", [message UTF8String]); +} + +- (NSInteger)runTestsWithOptions:(NSDictionary*)options inDirectory:(NSString*)path { + GWS_DCHECK([NSThread isMainThread]); + NSArray* ignoredHeaders = @[ @"Date", @"Etag" ]; // Dates are always different by definition and ETags depend on file system node IDs + NSInteger result = -1; + if ([self startWithOptions:options error:NULL]) { + _ExecuteMainThreadRunLoopSources(); + + result = 0; + NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL]; + for (NSString* requestFile in files) { + if (![requestFile hasSuffix:@".request"]) { + continue; + } + @autoreleasepool { + NSString* index = [[requestFile componentsSeparatedByString:@"-"] firstObject]; + BOOL success = NO; + NSData* requestData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:requestFile]]; + if (requestData) { + CFHTTPMessageRef request = _CreateHTTPMessageFromData(requestData, YES); + if (request) { + NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(request)); + NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(request)); + _LogResult(@"[%i] %@ %@", (int)[index integerValue], requestMethod, requestURL.path); + NSString* prefix = [index stringByAppendingString:@"-"]; + for (NSString* responseFile in files) { + if ([responseFile hasPrefix:prefix] && [responseFile hasSuffix:@".response"]) { + NSData* responseData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:responseFile]]; + if (responseData) { + CFHTTPMessageRef expectedResponse = _CreateHTTPMessageFromData(responseData, NO); + if (expectedResponse) { + CFHTTPMessageRef actualResponse = _CreateHTTPMessageFromPerformingRequest(requestData, self.port); + if (actualResponse) { + success = YES; + + CFIndex expectedStatusCode = CFHTTPMessageGetResponseStatusCode(expectedResponse); + CFIndex actualStatusCode = CFHTTPMessageGetResponseStatusCode(actualResponse); + if (actualStatusCode != expectedStatusCode) { + _LogResult(@" Status code not matching:\n Expected: %i\n Actual: %i", (int)expectedStatusCode, (int)actualStatusCode); + success = NO; + } + + NSDictionary* expectedHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(expectedResponse)); + NSDictionary* actualHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(actualResponse)); + for (NSString* expectedHeader in expectedHeaders) { + if ([ignoredHeaders containsObject:expectedHeader]) { + continue; + } + NSString* expectedValue = [expectedHeaders objectForKey:expectedHeader]; + NSString* actualValue = [actualHeaders objectForKey:expectedHeader]; + if (![actualValue isEqualToString:expectedValue]) { + _LogResult(@" Header '%@' not matching:\n Expected: \"%@\"\n Actual: \"%@\"", expectedHeader, expectedValue, actualValue); + success = NO; + } + } + for (NSString* actualHeader in actualHeaders) { + if (![expectedHeaders objectForKey:actualHeader]) { + _LogResult(@" Header '%@' not matching:\n Expected: \"%@\"\n Actual: \"%@\"", actualHeader, nil, [actualHeaders objectForKey:actualHeader]); + success = NO; + } + } + + NSString* expectedContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(expectedResponse, CFSTR("Content-Length"))); + NSData* expectedBody = CFBridgingRelease(CFHTTPMessageCopyBody(expectedResponse)); + NSString* actualContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(actualResponse, CFSTR("Content-Length"))); + NSData* actualBody = CFBridgingRelease(CFHTTPMessageCopyBody(actualResponse)); + if ([actualContentLength isEqualToString:expectedContentLength] && (actualBody.length > expectedBody.length)) { // Handle web browser closing connection before retrieving entire body (e.g. when playing a video file) + actualBody = [actualBody subdataWithRange:NSMakeRange(0, expectedBody.length)]; + } + if (![actualBody isEqualToData:expectedBody]) { + _LogResult(@" Bodies not matching:\n Expected: %lu bytes\n Actual: %lu bytes", (unsigned long)expectedBody.length, (unsigned long)actualBody.length); + success = NO; +#if !TARGET_OS_IPHONE +#if DEBUG + if (GCDWebServerIsTextContentType((NSString*)[expectedHeaders objectForKey:@"Content-Type"])) { + NSString* expectedPath = [NSTemporaryDirectory() stringByAppendingPathComponent:(NSString*)[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]]; + NSString* actualPath = [NSTemporaryDirectory() stringByAppendingPathComponent:(NSString*)[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]]; + if ([expectedBody writeToFile:expectedPath atomically:YES] && [actualBody writeToFile:actualPath atomically:YES]) { + NSTask* task = [[NSTask alloc] init]; + [task setLaunchPath:@"/usr/bin/opendiff"]; + [task setArguments:@[ expectedPath, actualPath ]]; + [task launch]; + } + } +#endif +#endif + } + + CFRelease(actualResponse); + } + CFRelease(expectedResponse); + } + } else { + GWS_DNOT_REACHED(); + } + break; + } + } + CFRelease(request); + } + } else { + GWS_DNOT_REACHED(); + } + _LogResult(@""); + if (!success) { + ++result; + } + } + _ExecuteMainThreadRunLoopSources(); + } + + [self stop]; + + _ExecuteMainThreadRunLoopSources(); + } + return result; +} + +@end + +#endif diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerConnection.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerConnection.h new file mode 100644 index 0000000..420d12a --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerConnection.h @@ -0,0 +1,183 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GCDWebServer.h" + +NS_ASSUME_NONNULL_BEGIN + +@class GCDWebServerHandler; + +/** + * The GCDWebServerConnection class is instantiated by GCDWebServer to handle + * each new HTTP connection. Each instance stays alive until the connection is + * closed. + * + * You cannot use this class directly, but it is made public so you can + * subclass it to override some hooks. Use the GCDWebServerOption_ConnectionClass + * option for GCDWebServer to install your custom subclass. + * + * @warning The GCDWebServerConnection retains the GCDWebServer until the + * connection is closed. + */ +@interface GCDWebServerConnection : NSObject + +/** + * Returns the GCDWebServer that owns the connection. + */ +@property(nonatomic, readonly) GCDWebServer* server; + +/** + * Returns YES if the connection is using IPv6. + */ +@property(nonatomic, readonly, getter=isUsingIPv6) BOOL usingIPv6; + +/** + * Returns the address of the local peer (i.e. server) of the connection + * as a raw "struct sockaddr". + */ +@property(nonatomic, readonly) NSData* localAddressData; + +/** + * Returns the address of the local peer (i.e. server) of the connection + * as a string. + */ +@property(nonatomic, readonly) NSString* localAddressString; + +/** + * Returns the address of the remote peer (i.e. client) of the connection + * as a raw "struct sockaddr". + */ +@property(nonatomic, readonly) NSData* remoteAddressData; + +/** + * Returns the address of the remote peer (i.e. client) of the connection + * as a string. + */ +@property(nonatomic, readonly) NSString* remoteAddressString; + +/** + * Returns the total number of bytes received from the remote peer (i.e. client) + * so far. + */ +@property(nonatomic, readonly) NSUInteger totalBytesRead; + +/** + * Returns the total number of bytes sent to the remote peer (i.e. client) so far. + */ +@property(nonatomic, readonly) NSUInteger totalBytesWritten; + +@end + +/** + * Hooks to customize the behavior of GCDWebServer HTTP connections. + * + * @warning These methods can be called on any GCD thread. + * Be sure to also call "super" when overriding them. + */ +@interface GCDWebServerConnection (Subclassing) + +/** + * This method is called when the connection is opened. + * + * Return NO to reject the connection e.g. after validating the local + * or remote address. + */ +- (BOOL)open; + +/** + * This method is called whenever data has been received + * from the remote peer (i.e. client). + * + * @warning Do not attempt to modify this data. + */ +- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length; + +/** + * This method is called whenever data has been sent + * to the remote peer (i.e. client). + * + * @warning Do not attempt to modify this data. + */ +- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length; + +/** + * This method is called after the HTTP headers have been received to + * allow replacing the request URL by another one. + * + * The default implementation returns the original URL. + */ +- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary*)headers; + +/** + * Assuming a valid HTTP request was received, this method is called before + * the request is processed. + * + * Return a non-nil GCDWebServerResponse to bypass the request processing entirely. + * + * The default implementation checks for HTTP authentication if applicable + * and returns a barebone 401 status code response if authentication failed. + */ +- (nullable GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request; + +/** + * Assuming a valid HTTP request was received and -preflightRequest: returned nil, + * this method is called to process the request by executing the handler's + * process block. + */ +- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion; + +/** + * Assuming a valid HTTP request was received and either -preflightRequest: + * or -processRequest:completion: returned a non-nil GCDWebServerResponse, + * this method is called to override the response. + * + * You can either modify the current response and return it, or return a + * completely new one. + * + * The default implementation replaces any response matching the "ETag" or + * "Last-Modified-Date" header of the request by a barebone "Not-Modified" (304) + * one. + */ +- (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request; + +/** + * This method is called if any error happens while validing or processing + * the request or if no GCDWebServerResponse was generated during processing. + * + * @warning If the request was invalid (e.g. the HTTP headers were malformed), + * the "request" argument will be nil. + */ +- (void)abortRequest:(nullable GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode; + +/** + * Called when the connection is closed. + */ +- (void)close; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerConnection.m b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerConnection.m new file mode 100644 index 0000000..1fd56c3 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerConnection.m @@ -0,0 +1,871 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error GCDWebServer requires ARC +#endif + +#import +#import +#ifdef __GCDWEBSERVER_ENABLE_TESTING__ +#import +#endif + +#import "GCDWebServerPrivate.h" + +#define kHeadersReadCapacity (1 * 1024) +#define kBodyReadCapacity (256 * 1024) + +typedef void (^ReadDataCompletionBlock)(BOOL success); +typedef void (^ReadHeadersCompletionBlock)(NSData* extraData); +typedef void (^ReadBodyCompletionBlock)(BOOL success); + +typedef void (^WriteDataCompletionBlock)(BOOL success); +typedef void (^WriteHeadersCompletionBlock)(BOOL success); +typedef void (^WriteBodyCompletionBlock)(BOOL success); + +static NSData* _CRLFData = nil; +static NSData* _CRLFCRLFData = nil; +static NSData* _continueData = nil; +static NSData* _lastChunkData = nil; +static NSString* _digestAuthenticationNonce = nil; +#ifdef __GCDWEBSERVER_ENABLE_TESTING__ +static int32_t _connectionCounter = 0; +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface GCDWebServerConnection (Read) +- (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block; +- (void)readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block; +- (void)readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block; +- (void)readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block; +@end + +@interface GCDWebServerConnection (Write) +- (void)writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block; +- (void)writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block; +- (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block; +@end + +NS_ASSUME_NONNULL_END + +@implementation GCDWebServerConnection { + CFSocketNativeHandle _socket; + BOOL _virtualHEAD; + + CFHTTPMessageRef _requestMessage; + GCDWebServerRequest* _request; + GCDWebServerHandler* _handler; + CFHTTPMessageRef _responseMessage; + GCDWebServerResponse* _response; + NSInteger _statusCode; + + BOOL _opened; +#ifdef __GCDWEBSERVER_ENABLE_TESTING__ + NSUInteger _connectionIndex; + NSString* _requestPath; + int _requestFD; + NSString* _responsePath; + int _responseFD; +#endif +} + ++ (void)initialize { + if (_CRLFData == nil) { + _CRLFData = [[NSData alloc] initWithBytes:"\r\n" length:2]; + GWS_DCHECK(_CRLFData); + } + if (_CRLFCRLFData == nil) { + _CRLFCRLFData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; + GWS_DCHECK(_CRLFCRLFData); + } + if (_continueData == nil) { + CFHTTPMessageRef message = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 100, NULL, kCFHTTPVersion1_1); + _continueData = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(message)); + CFRelease(message); + GWS_DCHECK(_continueData); + } + if (_lastChunkData == nil) { + _lastChunkData = [[NSData alloc] initWithBytes:"0\r\n\r\n" length:5]; + } + if (_digestAuthenticationNonce == nil) { + CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); + _digestAuthenticationNonce = GCDWebServerComputeMD5Digest(@"%@", CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid))); + CFRelease(uuid); + } +} + +- (BOOL)isUsingIPv6 { + const struct sockaddr* localSockAddr = _localAddressData.bytes; + return (localSockAddr->sa_family == AF_INET6); +} + +- (void)_initializeResponseHeadersWithStatusCode:(NSInteger)statusCode { + _statusCode = statusCode; + _responseMessage = CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_1); + CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Connection"), CFSTR("Close")); + CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Server"), (__bridge CFStringRef)_server.serverName); + CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Date"), (__bridge CFStringRef)GCDWebServerFormatRFC822([NSDate date])); +} + +- (void)_startProcessingRequest { + GWS_DCHECK(_responseMessage == NULL); + + GCDWebServerResponse* preflightResponse = [self preflightRequest:_request]; + if (preflightResponse) { + [self _finishProcessingRequest:preflightResponse]; + } else { + [self processRequest:_request + completion:^(GCDWebServerResponse* processResponse) { + [self _finishProcessingRequest:processResponse]; + }]; + } +} + +// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html +- (void)_finishProcessingRequest:(GCDWebServerResponse*)response { + GWS_DCHECK(_responseMessage == NULL); + BOOL hasBody = NO; + + if (response) { + response = [self overrideResponse:response forRequest:_request]; + } + if (response) { + if ([response hasBody]) { + [response prepareForReading]; + hasBody = !_virtualHEAD; + } + NSError* error = nil; + if (hasBody && ![response performOpen:&error]) { + GWS_LOG_ERROR(@"Failed opening response body for socket %i: %@", _socket, error); + } else { + _response = response; + } + } + + if (_response) { + [self _initializeResponseHeadersWithStatusCode:_response.statusCode]; + if (_response.lastModifiedDate) { + CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Last-Modified"), (__bridge CFStringRef)GCDWebServerFormatRFC822((NSDate*)_response.lastModifiedDate)); + } + if (_response.eTag) { + CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("ETag"), (__bridge CFStringRef)_response.eTag); + } + if ((_response.statusCode >= 200) && (_response.statusCode < 300)) { + if (_response.cacheControlMaxAge > 0) { + CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), (__bridge CFStringRef)[NSString stringWithFormat:@"max-age=%i, public", (int)_response.cacheControlMaxAge]); + } else { + CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), CFSTR("no-cache")); + } + } + if (_response.contentType != nil) { + CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Type"), (__bridge CFStringRef)GCDWebServerNormalizeHeaderValue(_response.contentType)); + } + if (_response.contentLength != NSUIntegerMax) { + CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Length"), (__bridge CFStringRef)[NSString stringWithFormat:@"%lu", (unsigned long)_response.contentLength]); + } + if (_response.usesChunkedTransferEncoding) { + CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Transfer-Encoding"), CFSTR("chunked")); + } + [_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) { + CFHTTPMessageSetHeaderFieldValue(_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); + }]; + [self writeHeadersWithCompletionBlock:^(BOOL success) { + + if (success) { + if (hasBody) { + [self writeBodyWithCompletionBlock:^(BOOL successInner) { + + [_response performClose]; // TODO: There's nothing we can do on failure as headers have already been sent + + }]; + } + } else if (hasBody) { + [_response performClose]; + } + + }]; + } else { + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; + } +} + +- (void)_readBodyWithLength:(NSUInteger)length initialData:(NSData*)initialData { + NSError* error = nil; + if (![_request performOpen:&error]) { + GWS_LOG_ERROR(@"Failed opening request body for socket %i: %@", _socket, error); + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; + return; + } + + if (initialData.length) { + if (![_request performWriteData:initialData error:&error]) { + GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error); + if (![_request performClose:&error]) { + GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); + } + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; + return; + } + length -= initialData.length; + } + + if (length) { + [self readBodyWithRemainingLength:length + completionBlock:^(BOOL success) { + + NSError* localError = nil; + if ([_request performClose:&localError]) { + [self _startProcessingRequest]; + } else { + GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; + } + + }]; + } else { + if ([_request performClose:&error]) { + [self _startProcessingRequest]; + } else { + GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; + } + } +} + +- (void)_readChunkedBodyWithInitialData:(NSData*)initialData { + NSError* error = nil; + if (![_request performOpen:&error]) { + GWS_LOG_ERROR(@"Failed opening request body for socket %i: %@", _socket, error); + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; + return; + } + + NSMutableData* chunkData = [[NSMutableData alloc] initWithData:initialData]; + [self readNextBodyChunk:chunkData + completionBlock:^(BOOL success) { + + NSError* localError = nil; + if ([_request performClose:&localError]) { + [self _startProcessingRequest]; + } else { + GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; + } + + }]; +} + +- (void)_readRequestHeaders { + _requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true); + NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity]; + [self readHeaders:headersData + withCompletionBlock:^(NSData* extraData) { + + if (extraData) { + NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(_requestMessage)); // Method verbs are case-sensitive and uppercase + if (_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) { + requestMethod = @"GET"; + _virtualHEAD = YES; + } + NSDictionary* requestHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones + NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(_requestMessage)); + if (requestURL) { + requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders]; + GWS_DCHECK(requestURL); + } + NSString* urlPath = requestURL ? CFBridgingRelease(CFURLCopyPath((CFURLRef)requestURL)) : nil; // Don't use -[NSURL path] which strips the ending slash + if (urlPath == nil) { + urlPath = @"/"; // CFURLCopyPath() returns NULL for a relative URL with path "//" contrary to -[NSURL path] which returns "/" + } + NSString* requestPath = urlPath ? GCDWebServerUnescapeURLString(urlPath) : nil; + NSString* queryString = requestURL ? CFBridgingRelease(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped; + NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{}; + if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) { + for (_handler in _server.handlers) { + _request = _handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery); + if (_request) { + break; + } + } + if (_request) { + _request.localAddressData = self.localAddressData; + _request.remoteAddressData = self.remoteAddressData; + if ([_request hasBody]) { + [_request prepareForWriting]; + if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) { + NSString* expectHeader = [requestHeaders objectForKey:@"Expect"]; + if (expectHeader) { + if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing + [self writeData:_continueData + withCompletionBlock:^(BOOL success) { + + if (success) { + if (_request.usesChunkedTransferEncoding) { + [self _readChunkedBodyWithInitialData:extraData]; + } else { + [self _readBodyWithLength:_request.contentLength initialData:extraData]; + } + } + + }]; + } else { + GWS_LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", _socket); + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed]; + } + } else { + if (_request.usesChunkedTransferEncoding) { + [self _readChunkedBodyWithInitialData:extraData]; + } else { + [self _readBodyWithLength:_request.contentLength initialData:extraData]; + } + } + } else { + GWS_LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", _socket); + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest]; + } + } else { + [self _startProcessingRequest]; + } + } else { + _request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery]; + GWS_DCHECK(_request); + [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_NotImplemented]; + } + } else { + [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; + GWS_DNOT_REACHED(); + } + } else { + [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; + } + + }]; +} + +- (instancetype)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket { + if ((self = [super init])) { + _server = server; + _localAddressData = localAddress; + _remoteAddressData = remoteAddress; + _socket = socket; + GWS_LOG_DEBUG(@"Did open connection on socket %i", _socket); + + [_server willStartConnection:self]; + + if (![self open]) { + close(_socket); + return nil; + } + _opened = YES; + + [self _readRequestHeaders]; + } + return self; +} + +- (NSString*)localAddressString { + return GCDWebServerStringFromSockAddr(_localAddressData.bytes, YES); +} + +- (NSString*)remoteAddressString { + return GCDWebServerStringFromSockAddr(_remoteAddressData.bytes, YES); +} + +- (void)dealloc { + int result = close(_socket); + if (result != 0) { + GWS_LOG_ERROR(@"Failed closing socket %i for connection: %s (%i)", _socket, strerror(errno), errno); + } else { + GWS_LOG_DEBUG(@"Did close connection on socket %i", _socket); + } + + if (_opened) { + [self close]; + } + + [_server didEndConnection:self]; + + if (_requestMessage) { + CFRelease(_requestMessage); + } + + if (_responseMessage) { + CFRelease(_responseMessage); + } +} + +@end + +@implementation GCDWebServerConnection (Read) + +- (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block { + dispatch_read(_socket, length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t buffer, int error) { + + @autoreleasepool { + if (error == 0) { + size_t size = dispatch_data_get_size(buffer); + if (size > 0) { + NSUInteger originalLength = data.length; + dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) { + [data appendBytes:chunkBytes length:chunkSize]; + return true; + }); + [self didReadBytes:((char*)data.bytes + originalLength) length:(data.length - originalLength)]; + block(YES); + } else { + if (_totalBytesRead > 0) { + GWS_LOG_ERROR(@"No more data available on socket %i", _socket); + } else { + GWS_LOG_WARNING(@"No data received from socket %i", _socket); + } + block(NO); + } + } else { + GWS_LOG_ERROR(@"Error while reading from socket %i: %s (%i)", _socket, strerror(error), error); + block(NO); + } + } + + }); +} + +- (void)readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block { + GWS_DCHECK(_requestMessage); + [self readData:headersData + withLength:NSUIntegerMax + completionBlock:^(BOOL success) { + + if (success) { + NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)]; + if (range.location == NSNotFound) { + [self readHeaders:headersData withCompletionBlock:block]; + } else { + NSUInteger length = range.location + range.length; + if (CFHTTPMessageAppendBytes(_requestMessage, headersData.bytes, length)) { + if (CFHTTPMessageIsHeaderComplete(_requestMessage)) { + block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]); + } else { + GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", _socket); + block(nil); + } + } else { + GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", _socket); + block(nil); + } + } + } else { + block(nil); + } + + }]; +} + +- (void)readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block { + GWS_DCHECK([_request hasBody] && ![_request usesChunkedTransferEncoding]); + NSMutableData* bodyData = [[NSMutableData alloc] initWithCapacity:kBodyReadCapacity]; + [self readData:bodyData + withLength:length + completionBlock:^(BOOL success) { + + if (success) { + if (bodyData.length <= length) { + NSError* error = nil; + if ([_request performWriteData:bodyData error:&error]) { + NSUInteger remainingLength = length - bodyData.length; + if (remainingLength) { + [self readBodyWithRemainingLength:remainingLength completionBlock:block]; + } else { + block(YES); + } + } else { + GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error); + block(NO); + } + } else { + GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", _socket); + block(NO); + GWS_DNOT_REACHED(); + } + } else { + block(NO); + } + + }]; +} + +static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { + char buffer[size + 1]; + bcopy(bytes, buffer, size); + buffer[size] = 0; + char* end = NULL; + long result = strtol(buffer, &end, 16); + return ((end != NULL) && (*end == 0) && (result >= 0) ? result : NSNotFound); +} + +- (void)readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block { + GWS_DCHECK([_request hasBody] && [_request usesChunkedTransferEncoding]); + + while (1) { + NSRange range = [chunkData rangeOfData:_CRLFData options:0 range:NSMakeRange(0, chunkData.length)]; + if (range.location == NSNotFound) { + break; + } + NSRange extensionRange = [chunkData rangeOfData:[NSData dataWithBytes:";" length:1] options:0 range:NSMakeRange(0, range.location)]; // Ignore chunk extensions + NSUInteger length = _ScanHexNumber((char*)chunkData.bytes, extensionRange.location != NSNotFound ? extensionRange.location : range.location); + if (length != NSNotFound) { + if (length) { + if (chunkData.length < range.location + range.length + length + 2) { + break; + } + const char* ptr = (char*)chunkData.bytes + range.location + range.length + length; + if ((*ptr == '\r') && (*(ptr + 1) == '\n')) { + NSError* error = nil; + if ([_request performWriteData:[chunkData subdataWithRange:NSMakeRange(range.location + range.length, length)] error:&error]) { + [chunkData replaceBytesInRange:NSMakeRange(0, range.location + range.length + length + 2) withBytes:NULL length:0]; + } else { + GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error); + block(NO); + return; + } + } else { + GWS_LOG_ERROR(@"Missing terminating CRLF sequence for chunk reading request body on socket %i", _socket); + block(NO); + return; + } + } else { + NSRange trailerRange = [chunkData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(range.location, chunkData.length - range.location)]; // Ignore trailers + if (trailerRange.location != NSNotFound) { + block(YES); + return; + } + } + } else { + GWS_LOG_ERROR(@"Invalid chunk length reading request body on socket %i", _socket); + block(NO); + return; + } + } + + [self readData:chunkData + withLength:NSUIntegerMax + completionBlock:^(BOOL success) { + + if (success) { + [self readNextBodyChunk:chunkData completionBlock:block]; + } else { + block(NO); + } + + }]; +} + +@end + +@implementation GCDWebServerConnection (Write) + +- (void)writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block { + dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^{ + [data self]; // Keeps ARC from releasing data too early + }); + dispatch_write(_socket, buffer, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t remainingData, int error) { + + @autoreleasepool { + if (error == 0) { + GWS_DCHECK(remainingData == NULL); + [self didWriteBytes:data.bytes length:data.length]; + block(YES); + } else { + GWS_LOG_ERROR(@"Error while writing to socket %i: %s (%i)", _socket, strerror(error), error); + block(NO); + } + } + + }); +#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE + dispatch_release(buffer); +#endif +} + +- (void)writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block { + GWS_DCHECK(_responseMessage); + CFDataRef data = CFHTTPMessageCopySerializedMessage(_responseMessage); + [self writeData:(__bridge NSData*)data withCompletionBlock:block]; + CFRelease(data); +} + +- (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block { + GWS_DCHECK([_response hasBody]); + [_response performReadDataWithCompletion:^(NSData* data, NSError* error) { + + if (data) { + if (data.length) { + if (_response.usesChunkedTransferEncoding) { + const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String]; + size_t hexLength = strlen(hexString); + NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)]; + if (chunk == nil) { + GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", _socket, error); + block(NO); + return; + } + char* ptr = (char*)[(NSMutableData*)chunk mutableBytes]; + bcopy(hexString, ptr, hexLength); + ptr += hexLength; + *ptr++ = '\r'; + *ptr++ = '\n'; + bcopy(data.bytes, ptr, data.length); + ptr += data.length; + *ptr++ = '\r'; + *ptr = '\n'; + data = chunk; + } + [self writeData:data + withCompletionBlock:^(BOOL success) { + + if (success) { + [self writeBodyWithCompletionBlock:block]; + } else { + block(NO); + } + + }]; + } else { + if (_response.usesChunkedTransferEncoding) { + [self writeData:_lastChunkData + withCompletionBlock:^(BOOL success) { + + block(success); + + }]; + } else { + block(YES); + } + } + } else { + GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error); + block(NO); + } + + }]; +} + +@end + +@implementation GCDWebServerConnection (Subclassing) + +- (BOOL)open { +#ifdef __GCDWEBSERVER_ENABLE_TESTING__ + if (_server.recordingEnabled) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + _connectionIndex = OSAtomicIncrement32(&_connectionCounter); +#pragma clang diagnostic pop + + _requestPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; + _requestFD = open([_requestPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + GWS_DCHECK(_requestFD > 0); + + _responsePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; + _responseFD = open([_responsePath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + GWS_DCHECK(_responseFD > 0); + } +#endif + + return YES; +} + +- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length { + GWS_LOG_DEBUG(@"Connection received %lu bytes on socket %i", (unsigned long)length, _socket); + _totalBytesRead += length; + +#ifdef __GCDWEBSERVER_ENABLE_TESTING__ + if ((_requestFD > 0) && (write(_requestFD, bytes, length) != (ssize_t)length)) { + GWS_LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno); + close(_requestFD); + _requestFD = 0; + } +#endif +} + +- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length { + GWS_LOG_DEBUG(@"Connection sent %lu bytes on socket %i", (unsigned long)length, _socket); + _totalBytesWritten += length; + +#ifdef __GCDWEBSERVER_ENABLE_TESTING__ + if ((_responseFD > 0) && (write(_responseFD, bytes, length) != (ssize_t)length)) { + GWS_LOG_ERROR(@"Failed recording response data: %s (%i)", strerror(errno), errno); + close(_responseFD); + _responseFD = 0; + } +#endif +} + +- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary*)headers { + return url; +} + +// https://tools.ietf.org/html/rfc2617 +- (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request { + GWS_LOG_DEBUG(@"Connection on socket %i preflighting request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead); + GCDWebServerResponse* response = nil; + if (_server.authenticationBasicAccounts) { + __block BOOL authenticated = NO; + NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"]; + if ([authorizationHeader hasPrefix:@"Basic "]) { + NSString* basicAccount = [authorizationHeader substringFromIndex:6]; + [_server.authenticationBasicAccounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* digest, BOOL* stop) { + if ([basicAccount isEqualToString:digest]) { + authenticated = YES; + *stop = YES; + } + }]; + } + if (!authenticated) { + response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized]; + [response setValue:[NSString stringWithFormat:@"Basic realm=\"%@\"", _server.authenticationRealm] forAdditionalHeader:@"WWW-Authenticate"]; + } + } else if (_server.authenticationDigestAccounts) { + BOOL authenticated = NO; + BOOL isStaled = NO; + NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"]; + if ([authorizationHeader hasPrefix:@"Digest "]) { + NSString* realm = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"realm"); + if (realm && [_server.authenticationRealm isEqualToString:realm]) { + NSString* nonce = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"nonce"); + if ([nonce isEqualToString:_digestAuthenticationNonce]) { + NSString* username = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"username"); + NSString* uri = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"uri"); + NSString* actualResponse = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"response"); + NSString* ha1 = [_server.authenticationDigestAccounts objectForKey:username]; + NSString* ha2 = GCDWebServerComputeMD5Digest(@"%@:%@", request.method, uri); // We cannot use "request.path" as the query string is required + NSString* expectedResponse = GCDWebServerComputeMD5Digest(@"%@:%@:%@", ha1, _digestAuthenticationNonce, ha2); + if ([actualResponse isEqualToString:expectedResponse]) { + authenticated = YES; + } + } else if (nonce.length) { + isStaled = YES; + } + } + } + if (!authenticated) { + response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized]; + [response setValue:[NSString stringWithFormat:@"Digest realm=\"%@\", nonce=\"%@\"%@", _server.authenticationRealm, _digestAuthenticationNonce, isStaled ? @", stale=TRUE" : @""] forAdditionalHeader:@"WWW-Authenticate"]; // TODO: Support Quality of Protection ("qop") + } + } + return response; +} + +- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion { + GWS_LOG_DEBUG(@"Connection on socket %i processing request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead); + _handler.asyncProcessBlock(request, [completion copy]); +} + +// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25 +// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26 +static inline BOOL _CompareResources(NSString* responseETag, NSString* requestETag, NSDate* responseLastModified, NSDate* requestLastModified) { + if (requestLastModified && responseLastModified) { + if ([responseLastModified compare:requestLastModified] != NSOrderedDescending) { + return YES; + } + } + if (requestETag && responseETag) { // Per the specs "If-None-Match" must be checked after "If-Modified-Since" + if ([requestETag isEqualToString:@"*"]) { + return YES; + } + if ([responseETag isEqualToString:requestETag]) { + return YES; + } + } + return NO; +} + +- (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request { + if ((response.statusCode >= 200) && (response.statusCode < 300) && _CompareResources(response.eTag, request.ifNoneMatch, response.lastModifiedDate, request.ifModifiedSince)) { + NSInteger code = [request.method isEqualToString:@"HEAD"] || [request.method isEqualToString:@"GET"] ? kGCDWebServerHTTPStatusCode_NotModified : kGCDWebServerHTTPStatusCode_PreconditionFailed; + GCDWebServerResponse* newResponse = [GCDWebServerResponse responseWithStatusCode:code]; + newResponse.cacheControlMaxAge = response.cacheControlMaxAge; + newResponse.lastModifiedDate = response.lastModifiedDate; + newResponse.eTag = response.eTag; + GWS_DCHECK(newResponse); + return newResponse; + } + return response; +} + +- (void)abortRequest:(GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode { + GWS_DCHECK(_responseMessage == NULL); + GWS_DCHECK((statusCode >= 400) && (statusCode < 600)); + [self _initializeResponseHeadersWithStatusCode:statusCode]; + [self writeHeadersWithCompletionBlock:^(BOOL success) { + ; // Nothing more to do + }]; + GWS_LOG_DEBUG(@"Connection aborted with status code %i on socket %i", (int)statusCode, _socket); +} + +- (void)close { +#ifdef __GCDWEBSERVER_ENABLE_TESTING__ + if (_requestPath) { + BOOL success = NO; + NSError* error = nil; + if (_requestFD > 0) { + close(_requestFD); + NSString* name = [NSString stringWithFormat:@"%03lu-%@.request", (unsigned long)_connectionIndex, _virtualHEAD ? @"HEAD" : _request.method]; + success = [[NSFileManager defaultManager] moveItemAtPath:_requestPath toPath:[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:name] error:&error]; + } + if (!success) { + GWS_LOG_ERROR(@"Failed saving recorded request: %@", error); + GWS_DNOT_REACHED(); + } + unlink([_requestPath fileSystemRepresentation]); + } + + if (_responsePath) { + BOOL success = NO; + NSError* error = nil; + if (_responseFD > 0) { + close(_responseFD); + NSString* name = [NSString stringWithFormat:@"%03lu-%i.response", (unsigned long)_connectionIndex, (int)_statusCode]; + success = [[NSFileManager defaultManager] moveItemAtPath:_responsePath toPath:[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:name] error:&error]; + } + if (!success) { + GWS_LOG_ERROR(@"Failed saving recorded response: %@", error); + GWS_DNOT_REACHED(); + } + unlink([_responsePath fileSystemRepresentation]); + } +#endif + + if (_request) { + GWS_LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead, (unsigned long)_totalBytesWritten); + } else { + GWS_LOG_VERBOSE(@"[%@] %@ %i \"(invalid request)\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, (unsigned long)_totalBytesRead, (unsigned long)_totalBytesWritten); + } +} + +@end diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerFunctions.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerFunctions.h new file mode 100644 index 0000000..4235ecc --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerFunctions.h @@ -0,0 +1,109 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Converts a file extension to the corresponding MIME type. + * If there is no match, "application/octet-stream" is returned. + * + * Overrides allow to customize the built-in mapping from extensions to MIME + * types. Keys of the dictionary must be lowercased file extensions without + * the period, and the values must be the corresponding MIME types. + */ +NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary* _Nullable overrides); + +/** + * Add percent-escapes to a string so it can be used in a URL. + * The legal characters ":@/?&=+" are also escaped to ensure compatibility + * with URL encoded forms and URL queries. + */ +NSString* _Nullable GCDWebServerEscapeURLString(NSString* string); + +/** + * Unescapes a URL percent-encoded string. + */ +NSString* _Nullable GCDWebServerUnescapeURLString(NSString* string); + +/** + * Extracts the unescaped names and values from an + * "application/x-www-form-urlencoded" form. + * http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 + */ +NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form); + +/** + * On OS X, returns the IPv4 or IPv6 address as a string of the primary + * connected service or nil if not available. + * + * On iOS, returns the IPv4 or IPv6 address as a string of the WiFi + * interface if connected or nil otherwise. + */ +NSString* _Nullable GCDWebServerGetPrimaryIPAddress(BOOL useIPv6); + +/** + * Converts a date into a string using RFC822 formatting. + * https://tools.ietf.org/html/rfc822#section-5 + * https://tools.ietf.org/html/rfc1123#section-5.2.14 + */ +NSString* GCDWebServerFormatRFC822(NSDate* date); + +/** + * Converts a RFC822 formatted string into a date. + * https://tools.ietf.org/html/rfc822#section-5 + * https://tools.ietf.org/html/rfc1123#section-5.2.14 + * + * @warning Timezones other than GMT are not supported by this function. + */ +NSDate* _Nullable GCDWebServerParseRFC822(NSString* string); + +/** + * Converts a date into a string using IOS 8601 formatting. + * http://tools.ietf.org/html/rfc3339#section-5.6 + */ +NSString* GCDWebServerFormatISO8601(NSDate* date); + +/** + * Converts a ISO 8601 formatted string into a date. + * http://tools.ietf.org/html/rfc3339#section-5.6 + * + * @warning Only "calendar" variant is supported at this time and timezones + * other than GMT are not supported either. + */ +NSDate* _Nullable GCDWebServerParseISO8601(NSString* string); + +#ifdef __cplusplus +} +#endif + +NS_ASSUME_NONNULL_END diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerFunctions.m b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerFunctions.m new file mode 100644 index 0000000..2a1edfc --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerFunctions.m @@ -0,0 +1,316 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error GCDWebServer requires ARC +#endif + +#import +#if TARGET_OS_IPHONE +#import +#else +#import +#endif +#import + +#import +#import +#import + +#import "GCDWebServerPrivate.h" + +static NSDateFormatter* _dateFormatterRFC822 = nil; +static NSDateFormatter* _dateFormatterISO8601 = nil; +static dispatch_queue_t _dateFormatterQueue = NULL; + +// TODO: Handle RFC 850 and ANSI C's asctime() format +void GCDWebServerInitializeFunctions() { + GWS_DCHECK([NSThread isMainThread]); // NSDateFormatter should be initialized on main thread + if (_dateFormatterRFC822 == nil) { + _dateFormatterRFC822 = [[NSDateFormatter alloc] init]; + _dateFormatterRFC822.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; + _dateFormatterRFC822.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'"; + _dateFormatterRFC822.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; + GWS_DCHECK(_dateFormatterRFC822); + } + if (_dateFormatterISO8601 == nil) { + _dateFormatterISO8601 = [[NSDateFormatter alloc] init]; + _dateFormatterISO8601.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; + _dateFormatterISO8601.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'+00:00'"; + _dateFormatterISO8601.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; + GWS_DCHECK(_dateFormatterISO8601); + } + if (_dateFormatterQueue == NULL) { + _dateFormatterQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); + GWS_DCHECK(_dateFormatterQueue); + } +} + +NSString* GCDWebServerNormalizeHeaderValue(NSString* value) { + if (value) { + NSRange range = [value rangeOfString:@";"]; // Assume part before ";" separator is case-insensitive + if (range.location != NSNotFound) { + value = [[[value substringToIndex:range.location] lowercaseString] stringByAppendingString:[value substringFromIndex:range.location]]; + } else { + value = [value lowercaseString]; + } + } + return value; +} + +NSString* GCDWebServerTruncateHeaderValue(NSString* value) { + if (value) { + NSRange range = [value rangeOfString:@";"]; + if (range.location != NSNotFound) { + return [value substringToIndex:range.location]; + } + } + return value; +} + +NSString* GCDWebServerExtractHeaderValueParameter(NSString* value, NSString* name) { + NSString* parameter = nil; + if (value) { + NSScanner* scanner = [[NSScanner alloc] initWithString:value]; + [scanner setCaseSensitive:NO]; // Assume parameter names are case-insensitive + NSString* string = [NSString stringWithFormat:@"%@=", name]; + if ([scanner scanUpToString:string intoString:NULL]) { + [scanner scanString:string intoString:NULL]; + if ([scanner scanString:@"\"" intoString:NULL]) { + [scanner scanUpToString:@"\"" intoString:¶meter]; + } else { + [scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:¶meter]; + } + } + } + return parameter; +} + +// http://www.w3schools.com/tags/ref_charactersets.asp +NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset) { + NSStringEncoding encoding = kCFStringEncodingInvalidId; + if (charset) { + encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)charset)); + } + return (encoding != kCFStringEncodingInvalidId ? encoding : NSUTF8StringEncoding); +} + +NSString* GCDWebServerFormatRFC822(NSDate* date) { + __block NSString* string; + dispatch_sync(_dateFormatterQueue, ^{ + string = [_dateFormatterRFC822 stringFromDate:date]; + }); + return string; +} + +NSDate* GCDWebServerParseRFC822(NSString* string) { + __block NSDate* date; + dispatch_sync(_dateFormatterQueue, ^{ + date = [_dateFormatterRFC822 dateFromString:string]; + }); + return date; +} + +NSString* GCDWebServerFormatISO8601(NSDate* date) { + __block NSString* string; + dispatch_sync(_dateFormatterQueue, ^{ + string = [_dateFormatterISO8601 stringFromDate:date]; + }); + return string; +} + +NSDate* GCDWebServerParseISO8601(NSString* string) { + __block NSDate* date; + dispatch_sync(_dateFormatterQueue, ^{ + date = [_dateFormatterISO8601 dateFromString:string]; + }); + return date; +} + +BOOL GCDWebServerIsTextContentType(NSString* type) { + return ([type hasPrefix:@"text/"] || [type hasPrefix:@"application/json"] || [type hasPrefix:@"application/xml"]); +} + +NSString* GCDWebServerDescribeData(NSData* data, NSString* type) { + if (GCDWebServerIsTextContentType(type)) { + NSString* charset = GCDWebServerExtractHeaderValueParameter(type, @"charset"); + NSString* string = [[NSString alloc] initWithData:data encoding:GCDWebServerStringEncodingFromCharset(charset)]; + if (string) { + return string; + } + } + return [NSString stringWithFormat:@"<%lu bytes>", (unsigned long)data.length]; +} + +NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary* overrides) { + NSDictionary* builtInOverrides = @{ @"css" : @"text/css" }; + NSString* mimeType = nil; + extension = [extension lowercaseString]; + if (extension.length) { + mimeType = [overrides objectForKey:extension]; + if (mimeType == nil) { + mimeType = [builtInOverrides objectForKey:extension]; + } + if (mimeType == nil) { + CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL); + if (uti) { + mimeType = CFBridgingRelease(UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)); + CFRelease(uti); + } + } + } + return mimeType ? mimeType : kGCDWebServerDefaultMimeType; +} + +NSString* GCDWebServerEscapeURLString(NSString* string) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":@/?&=+"), kCFStringEncodingUTF8)); +#pragma clang diagnostic pop +} + +NSString* GCDWebServerUnescapeURLString(NSString* string) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)string, CFSTR(""), kCFStringEncodingUTF8)); +#pragma clang diagnostic pop +} + +NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) { + NSMutableDictionary* parameters = [NSMutableDictionary dictionary]; + NSScanner* scanner = [[NSScanner alloc] initWithString:form]; + [scanner setCharactersToBeSkipped:nil]; + while (1) { + NSString* key = nil; + if (![scanner scanUpToString:@"=" intoString:&key] || [scanner isAtEnd]) { + break; + } + [scanner setScanLocation:([scanner scanLocation] + 1)]; + + NSString* value = nil; + [scanner scanUpToString:@"&" intoString:&value]; + if (value == nil) { + value = @""; + } + + key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "]; + NSString* unescapedKey = key ? GCDWebServerUnescapeURLString(key) : nil; + value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "]; + NSString* unescapedValue = value ? GCDWebServerUnescapeURLString(value) : nil; + if (unescapedKey && unescapedValue) { + [parameters setObject:unescapedValue forKey:unescapedKey]; + } else { + GWS_LOG_WARNING(@"Failed parsing URL encoded form for key \"%@\" and value \"%@\"", key, value); + GWS_DNOT_REACHED(); + } + + if ([scanner isAtEnd]) { + break; + } + [scanner setScanLocation:([scanner scanLocation] + 1)]; + } + return parameters; +} + +NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService) { + char hostBuffer[NI_MAXHOST]; + char serviceBuffer[NI_MAXSERV]; + if (getnameinfo(addr, addr->sa_len, hostBuffer, sizeof(hostBuffer), serviceBuffer, sizeof(serviceBuffer), NI_NUMERICHOST | NI_NUMERICSERV | NI_NOFQDN) != 0) { +#if DEBUG + GWS_DNOT_REACHED(); +#else + return @""; +#endif + } + return includeService ? [NSString stringWithFormat:@"%s:%s", hostBuffer, serviceBuffer] : (NSString*)[NSString stringWithUTF8String:hostBuffer]; +} + +NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) { + NSString* address = nil; +#if TARGET_OS_IPHONE +#if !TARGET_IPHONE_SIMULATOR && !TARGET_OS_TV + const char* primaryInterface = "en0"; // WiFi interface on iOS +#endif +#else + const char* primaryInterface = NULL; + SCDynamicStoreRef store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("GCDWebServer"), NULL, NULL); + if (store) { + CFPropertyListRef info = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4")); // There is no equivalent for IPv6 but the primary interface should be the same + if (info) { + NSString* interface = [(__bridge NSDictionary*)info objectForKey:@"PrimaryInterface"]; + if (interface) { + primaryInterface = [[NSString stringWithString:interface] UTF8String]; // Copy string to auto-release pool + } + CFRelease(info); + } + CFRelease(store); + } + if (primaryInterface == NULL) { + primaryInterface = "lo0"; + } +#endif + struct ifaddrs* list; + if (getifaddrs(&list) >= 0) { + for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) { +#if TARGET_IPHONE_SIMULATOR || TARGET_OS_TV + // Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator + // Assumption holds for Apple TV running tvOS + if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1")) +#else + if (strcmp(ifap->ifa_name, primaryInterface)) +#endif + { + continue; + } + if ((ifap->ifa_flags & IFF_UP) && ((!useIPv6 && (ifap->ifa_addr->sa_family == AF_INET)) || (useIPv6 && (ifap->ifa_addr->sa_family == AF_INET6)))) { + address = GCDWebServerStringFromSockAddr(ifap->ifa_addr, NO); + break; + } + } + freeifaddrs(list); + } + return address; +} + +NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) { + va_list arguments; + va_start(arguments, format); + const char* string = [[[NSString alloc] initWithFormat:format arguments:arguments] UTF8String]; + va_end(arguments); + unsigned char md5[CC_MD5_DIGEST_LENGTH]; + CC_MD5(string, (CC_LONG)strlen(string), md5); + char buffer[2 * CC_MD5_DIGEST_LENGTH + 1]; + for (int i = 0; i < CC_MD5_DIGEST_LENGTH; ++i) { + unsigned char byte = md5[i]; + unsigned char byteHi = (byte & 0xF0) >> 4; + buffer[2 * i + 0] = byteHi >= 10 ? 'a' + byteHi - 10 : '0' + byteHi; + unsigned char byteLo = byte & 0x0F; + buffer[2 * i + 1] = byteLo >= 10 ? 'a' + byteLo - 10 : '0' + byteLo; + } + buffer[2 * CC_MD5_DIGEST_LENGTH] = 0; + return (NSString*)[NSString stringWithUTF8String:buffer]; +} diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerHTTPStatusCodes.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerHTTPStatusCodes.h new file mode 100644 index 0000000..6e98381 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerHTTPStatusCodes.h @@ -0,0 +1,116 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html +// http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml + +#import + +/** + * Convenience constants for "informational" HTTP status codes. + */ +typedef NS_ENUM(NSInteger, GCDWebServerInformationalHTTPStatusCode) { + kGCDWebServerHTTPStatusCode_Continue = 100, + kGCDWebServerHTTPStatusCode_SwitchingProtocols = 101, + kGCDWebServerHTTPStatusCode_Processing = 102 +}; + +/** + * Convenience constants for "successful" HTTP status codes. + */ +typedef NS_ENUM(NSInteger, GCDWebServerSuccessfulHTTPStatusCode) { + kGCDWebServerHTTPStatusCode_OK = 200, + kGCDWebServerHTTPStatusCode_Created = 201, + kGCDWebServerHTTPStatusCode_Accepted = 202, + kGCDWebServerHTTPStatusCode_NonAuthoritativeInformation = 203, + kGCDWebServerHTTPStatusCode_NoContent = 204, + kGCDWebServerHTTPStatusCode_ResetContent = 205, + kGCDWebServerHTTPStatusCode_PartialContent = 206, + kGCDWebServerHTTPStatusCode_MultiStatus = 207, + kGCDWebServerHTTPStatusCode_AlreadyReported = 208 +}; + +/** + * Convenience constants for "redirection" HTTP status codes. + */ +typedef NS_ENUM(NSInteger, GCDWebServerRedirectionHTTPStatusCode) { + kGCDWebServerHTTPStatusCode_MultipleChoices = 300, + kGCDWebServerHTTPStatusCode_MovedPermanently = 301, + kGCDWebServerHTTPStatusCode_Found = 302, + kGCDWebServerHTTPStatusCode_SeeOther = 303, + kGCDWebServerHTTPStatusCode_NotModified = 304, + kGCDWebServerHTTPStatusCode_UseProxy = 305, + kGCDWebServerHTTPStatusCode_TemporaryRedirect = 307, + kGCDWebServerHTTPStatusCode_PermanentRedirect = 308 +}; + +/** + * Convenience constants for "client error" HTTP status codes. + */ +typedef NS_ENUM(NSInteger, GCDWebServerClientErrorHTTPStatusCode) { + kGCDWebServerHTTPStatusCode_BadRequest = 400, + kGCDWebServerHTTPStatusCode_Unauthorized = 401, + kGCDWebServerHTTPStatusCode_PaymentRequired = 402, + kGCDWebServerHTTPStatusCode_Forbidden = 403, + kGCDWebServerHTTPStatusCode_NotFound = 404, + kGCDWebServerHTTPStatusCode_MethodNotAllowed = 405, + kGCDWebServerHTTPStatusCode_NotAcceptable = 406, + kGCDWebServerHTTPStatusCode_ProxyAuthenticationRequired = 407, + kGCDWebServerHTTPStatusCode_RequestTimeout = 408, + kGCDWebServerHTTPStatusCode_Conflict = 409, + kGCDWebServerHTTPStatusCode_Gone = 410, + kGCDWebServerHTTPStatusCode_LengthRequired = 411, + kGCDWebServerHTTPStatusCode_PreconditionFailed = 412, + kGCDWebServerHTTPStatusCode_RequestEntityTooLarge = 413, + kGCDWebServerHTTPStatusCode_RequestURITooLong = 414, + kGCDWebServerHTTPStatusCode_UnsupportedMediaType = 415, + kGCDWebServerHTTPStatusCode_RequestedRangeNotSatisfiable = 416, + kGCDWebServerHTTPStatusCode_ExpectationFailed = 417, + kGCDWebServerHTTPStatusCode_UnprocessableEntity = 422, + kGCDWebServerHTTPStatusCode_Locked = 423, + kGCDWebServerHTTPStatusCode_FailedDependency = 424, + kGCDWebServerHTTPStatusCode_UpgradeRequired = 426, + kGCDWebServerHTTPStatusCode_PreconditionRequired = 428, + kGCDWebServerHTTPStatusCode_TooManyRequests = 429, + kGCDWebServerHTTPStatusCode_RequestHeaderFieldsTooLarge = 431 +}; + +/** + * Convenience constants for "server error" HTTP status codes. + */ +typedef NS_ENUM(NSInteger, GCDWebServerServerErrorHTTPStatusCode) { + kGCDWebServerHTTPStatusCode_InternalServerError = 500, + kGCDWebServerHTTPStatusCode_NotImplemented = 501, + kGCDWebServerHTTPStatusCode_BadGateway = 502, + kGCDWebServerHTTPStatusCode_ServiceUnavailable = 503, + kGCDWebServerHTTPStatusCode_GatewayTimeout = 504, + kGCDWebServerHTTPStatusCode_HTTPVersionNotSupported = 505, + kGCDWebServerHTTPStatusCode_InsufficientStorage = 507, + kGCDWebServerHTTPStatusCode_LoopDetected = 508, + kGCDWebServerHTTPStatusCode_NotExtended = 510, + kGCDWebServerHTTPStatusCode_NetworkAuthenticationRequired = 511 +}; diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerPrivate.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerPrivate.h new file mode 100644 index 0000000..d726ec2 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerPrivate.h @@ -0,0 +1,224 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import +#import + +/** + * All GCDWebServer headers. + */ + +#import "GCDWebServerHTTPStatusCodes.h" +#import "GCDWebServerFunctions.h" + +#import "GCDWebServer.h" +#import "GCDWebServerConnection.h" + +#import "GCDWebServerDataRequest.h" +#import "GCDWebServerFileRequest.h" +#import "GCDWebServerMultiPartFormRequest.h" +#import "GCDWebServerURLEncodedFormRequest.h" + +#import "GCDWebServerDataResponse.h" +#import "GCDWebServerErrorResponse.h" +#import "GCDWebServerFileResponse.h" +#import "GCDWebServerStreamedResponse.h" + +/** + * Check if a custom logging facility should be used instead. + */ + +#if defined(__GCDWEBSERVER_LOGGING_HEADER__) + +#define __GCDWEBSERVER_LOGGING_FACILITY_CUSTOM__ + +#import __GCDWEBSERVER_LOGGING_HEADER__ + +/** + * Automatically detect if XLFacility is available and if so use it as a + * logging facility. + */ + +#elif defined(__has_include) && __has_include("XLFacilityMacros.h") + +#define __GCDWEBSERVER_LOGGING_FACILITY_XLFACILITY__ + +#undef XLOG_TAG +#define XLOG_TAG @"gcdwebserver.internal" + +#import "XLFacilityMacros.h" + +#define GWS_LOG_DEBUG(...) XLOG_DEBUG(__VA_ARGS__) +#define GWS_LOG_VERBOSE(...) XLOG_VERBOSE(__VA_ARGS__) +#define GWS_LOG_INFO(...) XLOG_INFO(__VA_ARGS__) +#define GWS_LOG_WARNING(...) XLOG_WARNING(__VA_ARGS__) +#define GWS_LOG_ERROR(...) XLOG_ERROR(__VA_ARGS__) + +#define GWS_DCHECK(__CONDITION__) XLOG_DEBUG_CHECK(__CONDITION__) +#define GWS_DNOT_REACHED() XLOG_DEBUG_UNREACHABLE() + +/** + * If all of the above fail, then use GCDWebServer built-in + * logging facility. + */ + +#else + +#define __GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__ + +typedef NS_ENUM(int, GCDWebServerLoggingLevel) { + kGCDWebServerLoggingLevel_Debug = 0, + kGCDWebServerLoggingLevel_Verbose, + kGCDWebServerLoggingLevel_Info, + kGCDWebServerLoggingLevel_Warning, + kGCDWebServerLoggingLevel_Error +}; + +extern GCDWebServerLoggingLevel GCDWebServerLogLevel; +extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* _Nonnull format, ...) NS_FORMAT_FUNCTION(2, 3); + +#if DEBUG +#define GWS_LOG_DEBUG(...) \ + do { \ + if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Debug) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Debug, __VA_ARGS__); \ + } while (0) +#else +#define GWS_LOG_DEBUG(...) +#endif +#define GWS_LOG_VERBOSE(...) \ + do { \ + if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Verbose) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Verbose, __VA_ARGS__); \ + } while (0) +#define GWS_LOG_INFO(...) \ + do { \ + if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Info) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Info, __VA_ARGS__); \ + } while (0) +#define GWS_LOG_WARNING(...) \ + do { \ + if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Warning) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Warning, __VA_ARGS__); \ + } while (0) +#define GWS_LOG_ERROR(...) \ + do { \ + if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Error) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Error, __VA_ARGS__); \ + } while (0) + +#endif + +/** + * Consistency check macros used when building Debug only. + */ + +#if !defined(GWS_DCHECK) || !defined(GWS_DNOT_REACHED) + +#if DEBUG + +#define GWS_DCHECK(__CONDITION__) \ + do { \ + if (!(__CONDITION__)) { \ + abort(); \ + } \ + } while (0) +#define GWS_DNOT_REACHED() abort() + +#else + +#define GWS_DCHECK(__CONDITION__) +#define GWS_DNOT_REACHED() + +#endif + +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + * GCDWebServer internal constants and APIs. + */ + +#define kGCDWebServerDefaultMimeType @"application/octet-stream" +#define kGCDWebServerErrorDomain @"GCDWebServerErrorDomain" + +static inline BOOL GCDWebServerIsValidByteRange(NSRange range) { + return ((range.location != NSUIntegerMax) || (range.length > 0)); +} + +static inline NSError* GCDWebServerMakePosixError(int code) { + return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : (NSString*)[NSString stringWithUTF8String:strerror(code)]}]; +} + +extern void GCDWebServerInitializeFunctions(); +extern NSString* _Nullable GCDWebServerNormalizeHeaderValue(NSString* _Nullable value); +extern NSString* _Nullable GCDWebServerTruncateHeaderValue(NSString* _Nullable value); +extern NSString* _Nullable GCDWebServerExtractHeaderValueParameter(NSString* _Nullable value, NSString* attribute); +extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset); +extern BOOL GCDWebServerIsTextContentType(NSString* type); +extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType); +extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2); +extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService); + +@interface GCDWebServerConnection () +- (instancetype)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket; +@end + +@interface GCDWebServer () +@property(nonatomic, readonly) NSMutableArray* handlers; +@property(nonatomic, readonly, nullable) NSString* serverName; +@property(nonatomic, readonly, nullable) NSString* authenticationRealm; +@property(nonatomic, readonly, nullable) NSMutableDictionary* authenticationBasicAccounts; +@property(nonatomic, readonly, nullable) NSMutableDictionary* authenticationDigestAccounts; +@property(nonatomic, readonly) BOOL shouldAutomaticallyMapHEADToGET; +@property(nonatomic, readonly) dispatch_queue_priority_t dispatchQueuePriority; +- (void)willStartConnection:(GCDWebServerConnection*)connection; +- (void)didEndConnection:(GCDWebServerConnection*)connection; +@end + +@interface GCDWebServerHandler : NSObject +@property(nonatomic, readonly) GCDWebServerMatchBlock matchBlock; +@property(nonatomic, readonly) GCDWebServerAsyncProcessBlock asyncProcessBlock; +@end + +@interface GCDWebServerRequest () +@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding; +@property(nonatomic) NSData* localAddressData; +@property(nonatomic) NSData* remoteAddressData; +- (void)prepareForWriting; +- (BOOL)performOpen:(NSError**)error; +- (BOOL)performWriteData:(NSData*)data error:(NSError**)error; +- (BOOL)performClose:(NSError**)error; +- (void)setAttribute:(nullable id)attribute forKey:(NSString*)key; +@end + +@interface GCDWebServerResponse () +@property(nonatomic, readonly) NSDictionary* additionalHeaders; +@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding; +- (void)prepareForReading; +- (BOOL)performOpen:(NSError**)error; +- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block; +- (void)performClose; +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerRequest.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerRequest.h new file mode 100644 index 0000000..3fe9029 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerRequest.h @@ -0,0 +1,210 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Attribute key to retrieve an NSArray containing NSStrings from a GCDWebServerRequest + * with the contents of any regular expression captures done on the request path. + * + * @warning This attribute will only be set on the request if adding a handler using + * -addHandlerForMethod:pathRegex:requestClass:processBlock:. + */ +extern NSString* const GCDWebServerRequestAttribute_RegexCaptures; + +/** + * This protocol is used by the GCDWebServerConnection to communicate with + * the GCDWebServerRequest and write the received HTTP body data. + * + * Note that multiple GCDWebServerBodyWriter objects can be chained together + * internally e.g. to automatically decode gzip encoded content before + * passing it on to the GCDWebServerRequest. + * + * @warning These methods can be called on any GCD thread. + */ +@protocol GCDWebServerBodyWriter + +/** + * This method is called before any body data is received. + * + * It should return YES on success or NO on failure and set the "error" argument + * which is guaranteed to be non-NULL. + */ +- (BOOL)open:(NSError**)error; + +/** + * This method is called whenever body data has been received. + * + * It should return YES on success or NO on failure and set the "error" argument + * which is guaranteed to be non-NULL. + */ +- (BOOL)writeData:(NSData*)data error:(NSError**)error; + +/** + * This method is called after all body data has been received. + * + * It should return YES on success or NO on failure and set the "error" argument + * which is guaranteed to be non-NULL. + */ +- (BOOL)close:(NSError**)error; + +@end + +/** + * The GCDWebServerRequest class is instantiated by the GCDWebServerConnection + * after the HTTP headers have been received. Each instance wraps a single HTTP + * request. If a body is present, the methods from the GCDWebServerBodyWriter + * protocol will be called by the GCDWebServerConnection to receive it. + * + * The default implementation of the GCDWebServerBodyWriter protocol on the class + * simply ignores the body data. + * + * @warning GCDWebServerRequest instances can be created and used on any GCD thread. + */ +@interface GCDWebServerRequest : NSObject + +/** + * Returns the HTTP method for the request. + */ +@property(nonatomic, readonly) NSString* method; + +/** + * Returns the URL for the request. + */ +@property(nonatomic, readonly) NSURL* URL; + +/** + * Returns the HTTP headers for the request. + */ +@property(nonatomic, readonly) NSDictionary* headers; + +/** + * Returns the path component of the URL for the request. + */ +@property(nonatomic, readonly) NSString* path; + +/** + * Returns the parsed and unescaped query component of the URL for the request. + * + * @warning This property will be nil if there is no query in the URL. + */ +@property(nonatomic, readonly, nullable) NSDictionary* query; + +/** + * Returns the content type for the body of the request parsed from the + * "Content-Type" header. + * + * This property will be nil if the request has no body or set to + * "application/octet-stream" if a body is present but there was no + * "Content-Type" header. + */ +@property(nonatomic, readonly, nullable) NSString* contentType; + +/** + * Returns the content length for the body of the request parsed from the + * "Content-Length" header. + * + * This property will be set to "NSUIntegerMax" if the request has no body or + * if there is a body but no "Content-Length" header, typically because + * chunked transfer encoding is used. + */ +@property(nonatomic, readonly) NSUInteger contentLength; + +/** + * Returns the parsed "If-Modified-Since" header or nil if absent or malformed. + */ +@property(nonatomic, readonly, nullable) NSDate* ifModifiedSince; + +/** + * Returns the parsed "If-None-Match" header or nil if absent or malformed. + */ +@property(nonatomic, readonly, nullable) NSString* ifNoneMatch; + +/** + * Returns the parsed "Range" header or (NSUIntegerMax, 0) if absent or malformed. + * The range will be set to (offset, length) if expressed from the beginning + * of the entity body, or (NSUIntegerMax, length) if expressed from its end. + */ +@property(nonatomic, readonly) NSRange byteRange; + +/** + * Returns YES if the client supports gzip content encoding according to the + * "Accept-Encoding" header. + */ +@property(nonatomic, readonly) BOOL acceptsGzipContentEncoding; + +/** + * Returns the address of the local peer (i.e. server) for the request + * as a raw "struct sockaddr". + */ +@property(nonatomic, readonly) NSData* localAddressData; + +/** + * Returns the address of the local peer (i.e. server) for the request + * as a string. + */ +@property(nonatomic, readonly) NSString* localAddressString; + +/** + * Returns the address of the remote peer (i.e. client) for the request + * as a raw "struct sockaddr". + */ +@property(nonatomic, readonly) NSData* remoteAddressData; + +/** + * Returns the address of the remote peer (i.e. client) for the request + * as a string. + */ +@property(nonatomic, readonly) NSString* remoteAddressString; + +/** + * This method is the designated initializer for the class. + */ +- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(nullable NSDictionary*)query; + +/** + * Convenience method that checks if the contentType property is defined. + */ +- (BOOL)hasBody; + +/** + * Convenience method that checks if the byteRange property is defined. + */ +- (BOOL)hasByteRange; + +/** + * Retrieves an attribute associated with this request using the given key. + * + * @return The attribute value for the key. + */ +- (nullable id)attributeForKey:(NSString*)key; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerRequest.m b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerRequest.m new file mode 100644 index 0000000..05988cd --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerRequest.m @@ -0,0 +1,303 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error GCDWebServer requires ARC +#endif + +#import + +#import "GCDWebServerPrivate.h" + +NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerRequestAttribute_RegexCaptures"; + +#define kZlibErrorDomain @"ZlibErrorDomain" +#define kGZipInitialBufferSize (256 * 1024) + +@interface GCDWebServerBodyDecoder : NSObject +@end + +@interface GCDWebServerGZipDecoder : GCDWebServerBodyDecoder +@end + +@implementation GCDWebServerBodyDecoder { + GCDWebServerRequest* __unsafe_unretained _request; + id __unsafe_unretained _writer; +} + +- (instancetype)initWithRequest:(GCDWebServerRequest* _Nonnull)request writer:(id _Nonnull)writer { + if ((self = [super init])) { + _request = request; + _writer = writer; + } + return self; +} + +- (BOOL)open:(NSError**)error { + return [_writer open:error]; +} + +- (BOOL)writeData:(NSData*)data error:(NSError**)error { + return [_writer writeData:data error:error]; +} + +- (BOOL)close:(NSError**)error { + return [_writer close:error]; +} + +@end + +@implementation GCDWebServerGZipDecoder { + z_stream _stream; + BOOL _finished; +} + +- (BOOL)open:(NSError**)error { + int result = inflateInit2(&_stream, 15 + 16); + if (result != Z_OK) { + if (error) { + *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; + } + return NO; + } + if (![super open:error]) { + inflateEnd(&_stream); + return NO; + } + return YES; +} + +- (BOOL)writeData:(NSData*)data error:(NSError**)error { + GWS_DCHECK(!_finished); + _stream.next_in = (Bytef*)data.bytes; + _stream.avail_in = (uInt)data.length; + NSMutableData* decodedData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize]; + if (decodedData == nil) { + GWS_DNOT_REACHED(); + return NO; + } + NSUInteger length = 0; + while (1) { + NSUInteger maxLength = decodedData.length - length; + _stream.next_out = (Bytef*)((char*)decodedData.mutableBytes + length); + _stream.avail_out = (uInt)maxLength; + int result = inflate(&_stream, Z_NO_FLUSH); + if ((result != Z_OK) && (result != Z_STREAM_END)) { + if (error) { + *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; + } + return NO; + } + length += maxLength - _stream.avail_out; + if (_stream.avail_out > 0) { + if (result == Z_STREAM_END) { + _finished = YES; + } + break; + } + decodedData.length = 2 * decodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available + } + decodedData.length = length; + BOOL success = length ? [super writeData:decodedData error:error] : YES; // No need to call writer if we have no data yet + return success; +} + +- (BOOL)close:(NSError**)error { + GWS_DCHECK(_finished); + inflateEnd(&_stream); + return [super close:error]; +} + +@end + +@implementation GCDWebServerRequest { + BOOL _opened; + NSMutableArray* _decoders; + id __unsafe_unretained _writer; + NSMutableDictionary* _attributes; +} + +- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { + if ((self = [super init])) { + _method = [method copy]; + _URL = url; + _headers = headers; + _path = [path copy]; + _query = query; + + _contentType = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Content-Type"]); + _usesChunkedTransferEncoding = [GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Transfer-Encoding"]) isEqualToString:@"chunked"]; + NSString* lengthHeader = [_headers objectForKey:@"Content-Length"]; + if (lengthHeader) { + NSInteger length = [lengthHeader integerValue]; + if (_usesChunkedTransferEncoding || (length < 0)) { + GWS_LOG_WARNING(@"Invalid 'Content-Length' header '%@' for '%@' request on \"%@\"", lengthHeader, _method, _URL); + GWS_DNOT_REACHED(); + return nil; + } + _contentLength = length; + if (_contentType == nil) { + _contentType = kGCDWebServerDefaultMimeType; + } + } else if (_usesChunkedTransferEncoding) { + if (_contentType == nil) { + _contentType = kGCDWebServerDefaultMimeType; + } + _contentLength = NSUIntegerMax; + } else { + if (_contentType) { + GWS_LOG_WARNING(@"Ignoring 'Content-Type' header for '%@' request on \"%@\"", _method, _URL); + _contentType = nil; // Content-Type without Content-Length or chunked-encoding doesn't make sense + } + _contentLength = NSUIntegerMax; + } + + NSString* modifiedHeader = [_headers objectForKey:@"If-Modified-Since"]; + if (modifiedHeader) { + _ifModifiedSince = [GCDWebServerParseRFC822(modifiedHeader) copy]; + } + _ifNoneMatch = [_headers objectForKey:@"If-None-Match"]; + + _byteRange = NSMakeRange(NSUIntegerMax, 0); + NSString* rangeHeader = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Range"]); + if (rangeHeader) { + if ([rangeHeader hasPrefix:@"bytes="]) { + NSArray* components = [[rangeHeader substringFromIndex:6] componentsSeparatedByString:@","]; + if (components.count == 1) { + components = [[components firstObject] componentsSeparatedByString:@"-"]; + if (components.count == 2) { + NSString* startString = [components objectAtIndex:0]; + NSInteger startValue = [startString integerValue]; + NSString* endString = [components objectAtIndex:1]; + NSInteger endValue = [endString integerValue]; + if (startString.length && (startValue >= 0) && endString.length && (endValue >= startValue)) { // The second 500 bytes: "500-999" + _byteRange.location = startValue; + _byteRange.length = endValue - startValue + 1; + } else if (startString.length && (startValue >= 0)) { // The bytes after 9500 bytes: "9500-" + _byteRange.location = startValue; + _byteRange.length = NSUIntegerMax; + } else if (endString.length && (endValue > 0)) { // The final 500 bytes: "-500" + _byteRange.location = NSUIntegerMax; + _byteRange.length = endValue; + } + } + } + } + if ((_byteRange.location == NSUIntegerMax) && (_byteRange.length == 0)) { // Ignore "Range" header if syntactically invalid + GWS_LOG_WARNING(@"Failed to parse 'Range' header \"%@\" for url: %@", rangeHeader, url); + } + } + + if ([[_headers objectForKey:@"Accept-Encoding"] rangeOfString:@"gzip"].location != NSNotFound) { + _acceptsGzipContentEncoding = YES; + } + + _decoders = [[NSMutableArray alloc] init]; + _attributes = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (BOOL)hasBody { + return _contentType ? YES : NO; +} + +- (BOOL)hasByteRange { + return GCDWebServerIsValidByteRange(_byteRange); +} + +- (id)attributeForKey:(NSString*)key { + return [_attributes objectForKey:key]; +} + +- (BOOL)open:(NSError**)error { + return YES; +} + +- (BOOL)writeData:(NSData*)data error:(NSError**)error { + return YES; +} + +- (BOOL)close:(NSError**)error { + return YES; +} + +- (void)prepareForWriting { + _writer = self; + if ([GCDWebServerNormalizeHeaderValue([self.headers objectForKey:@"Content-Encoding"]) isEqualToString:@"gzip"]) { + GCDWebServerGZipDecoder* decoder = [[GCDWebServerGZipDecoder alloc] initWithRequest:self writer:_writer]; + [_decoders addObject:decoder]; + _writer = decoder; + } +} + +- (BOOL)performOpen:(NSError**)error { + GWS_DCHECK(_contentType); + GWS_DCHECK(_writer); + if (_opened) { + GWS_DNOT_REACHED(); + return NO; + } + _opened = YES; + return [_writer open:error]; +} + +- (BOOL)performWriteData:(NSData*)data error:(NSError**)error { + GWS_DCHECK(_opened); + return [_writer writeData:data error:error]; +} + +- (BOOL)performClose:(NSError**)error { + GWS_DCHECK(_opened); + return [_writer close:error]; +} + +- (void)setAttribute:(id)attribute forKey:(NSString*)key { + [_attributes setValue:attribute forKey:key]; +} + +- (NSString*)localAddressString { + return GCDWebServerStringFromSockAddr(_localAddressData.bytes, YES); +} + +- (NSString*)remoteAddressString { + return GCDWebServerStringFromSockAddr(_remoteAddressData.bytes, YES); +} + +- (NSString*)description { + NSMutableString* description = [NSMutableString stringWithFormat:@"%@ %@", _method, _path]; + for (NSString* argument in [[_query allKeys] sortedArrayUsingSelector:@selector(compare:)]) { + [description appendFormat:@"\n %@ = %@", argument, [_query objectForKey:argument]]; + } + [description appendString:@"\n"]; + for (NSString* header in [[_headers allKeys] sortedArrayUsingSelector:@selector(compare:)]) { + [description appendFormat:@"\n%@: %@", header, [_headers objectForKey:header]]; + } + return description; +} + +@end diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerResponse.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerResponse.h new file mode 100644 index 0000000..1e5e8c9 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerResponse.h @@ -0,0 +1,212 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * The GCDWebServerBodyReaderCompletionBlock is passed by GCDWebServer to the + * GCDWebServerBodyReader object when reading data from it asynchronously. + */ +typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* _Nullable error); + +/** + * This protocol is used by the GCDWebServerConnection to communicate with + * the GCDWebServerResponse and read the HTTP body data to send. + * + * Note that multiple GCDWebServerBodyReader objects can be chained together + * internally e.g. to automatically apply gzip encoding to the content before + * passing it on to the GCDWebServerResponse. + * + * @warning These methods can be called on any GCD thread. + */ +@protocol GCDWebServerBodyReader + +@required + +/** + * This method is called before any body data is sent. + * + * It should return YES on success or NO on failure and set the "error" argument + * which is guaranteed to be non-NULL. + */ +- (BOOL)open:(NSError**)error; + +/** + * This method is called whenever body data is sent. + * + * It should return a non-empty NSData if there is body data available, + * or an empty NSData there is no more body data, or nil on error and set + * the "error" argument which is guaranteed to be non-NULL. + */ +- (nullable NSData*)readData:(NSError**)error; + +/** + * This method is called after all body data has been sent. + */ +- (void)close; + +@optional + +/** + * If this method is implemented, it will be preferred over -readData:. + * + * It must call the passed block when data is available, passing a non-empty + * NSData if there is body data available, or an empty NSData there is no more + * body data, or nil on error and pass an NSError along. + */ +- (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block; + +@end + +/** + * The GCDWebServerResponse class is used to wrap a single HTTP response. + * It is instantiated by the handler of the GCDWebServer that handled the request. + * If a body is present, the methods from the GCDWebServerBodyReader protocol + * will be called by the GCDWebServerConnection to send it. + * + * The default implementation of the GCDWebServerBodyReader protocol + * on the class simply returns an empty body. + * + * @warning GCDWebServerResponse instances can be created and used on any GCD thread. + */ +@interface GCDWebServerResponse : NSObject + +/** + * Sets the content type for the body of the response. + * + * The default value is nil i.e. the response has no body. + * + * @warning This property must be set if a body is present. + */ +@property(nonatomic, copy, nullable) NSString* contentType; + +/** + * Sets the content length for the body of the response. If a body is present + * but this property is set to "NSUIntegerMax", this means the length of the body + * cannot be known ahead of time. Chunked transfer encoding will be + * automatically enabled by the GCDWebServerConnection to comply with HTTP/1.1 + * specifications. + * + * The default value is "NSUIntegerMax" i.e. the response has no body or its length + * is undefined. + */ +@property(nonatomic) NSUInteger contentLength; + +/** + * Sets the HTTP status code for the response. + * + * The default value is 200 i.e. "OK". + */ +@property(nonatomic) NSInteger statusCode; + +/** + * Sets the caching hint for the response using the "Cache-Control" header. + * This value is expressed in seconds. + * + * The default value is 0 i.e. "no-cache". + */ +@property(nonatomic) NSUInteger cacheControlMaxAge; + +/** + * Sets the last modified date for the response using the "Last-Modified" header. + * + * The default value is nil. + */ +@property(nonatomic, nullable) NSDate* lastModifiedDate; + +/** + * Sets the ETag for the response using the "ETag" header. + * + * The default value is nil. + */ +@property(nonatomic, copy, nullable) NSString* eTag; + +/** + * Enables gzip encoding for the response body. + * + * The default value is NO. + * + * @warning Enabling gzip encoding will remove any "Content-Length" header + * since the length of the body is not known anymore. The client will still + * be able to determine the body length when connection is closed per + * HTTP/1.1 specifications. + */ +@property(nonatomic, getter=isGZipContentEncodingEnabled) BOOL gzipContentEncodingEnabled; + +/** + * Creates an empty response. + */ ++ (instancetype)response; + +/** + * This method is the designated initializer for the class. + */ +- (instancetype)init; + +/** + * Sets an additional HTTP header on the response. + * Pass a nil value to remove an additional header. + * + * @warning Do not attempt to override the primary headers used + * by GCDWebServerResponse like "Content-Type", "ETag", etc... + */ +- (void)setValue:(nullable NSString*)value forAdditionalHeader:(NSString*)header; + +/** + * Convenience method that checks if the contentType property is defined. + */ +- (BOOL)hasBody; + +@end + +@interface GCDWebServerResponse (Extensions) + +/** + * Creates a empty response with a specific HTTP status code. + */ ++ (instancetype)responseWithStatusCode:(NSInteger)statusCode; + +/** + * Creates an HTTP redirect response to a new URL. + */ ++ (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent; + +/** + * Initializes an empty response with a specific HTTP status code. + */ +- (instancetype)initWithStatusCode:(NSInteger)statusCode; + +/** + * Initializes an HTTP redirect response to a new URL. + */ +- (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerResponse.m b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerResponse.m new file mode 100644 index 0000000..9153ff6 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Core/GCDWebServerResponse.m @@ -0,0 +1,284 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error GCDWebServer requires ARC +#endif + +#import + +#import "GCDWebServerPrivate.h" + +#define kZlibErrorDomain @"ZlibErrorDomain" +#define kGZipInitialBufferSize (256 * 1024) + +@interface GCDWebServerBodyEncoder : NSObject +@end + +@interface GCDWebServerGZipEncoder : GCDWebServerBodyEncoder +@end + +@implementation GCDWebServerBodyEncoder { + GCDWebServerResponse* __unsafe_unretained _response; + id __unsafe_unretained _reader; +} + +- (instancetype)initWithResponse:(GCDWebServerResponse* _Nonnull)response reader:(id _Nonnull)reader { + if ((self = [super init])) { + _response = response; + _reader = reader; + } + return self; +} + +- (BOOL)open:(NSError**)error { + return [_reader open:error]; +} + +- (NSData*)readData:(NSError**)error { + return [_reader readData:error]; +} + +- (void)close { + [_reader close]; +} + +@end + +@implementation GCDWebServerGZipEncoder { + z_stream _stream; + BOOL _finished; +} + +- (instancetype)initWithResponse:(GCDWebServerResponse* _Nonnull)response reader:(id _Nonnull)reader { + if ((self = [super initWithResponse:response reader:reader])) { + response.contentLength = NSUIntegerMax; // Make sure "Content-Length" header is not set since we don't know it + [response setValue:@"gzip" forAdditionalHeader:@"Content-Encoding"]; + } + return self; +} + +- (BOOL)open:(NSError**)error { + int result = deflateInit2(&_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); + if (result != Z_OK) { + if (error) { + *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; + } + return NO; + } + if (![super open:error]) { + deflateEnd(&_stream); + return NO; + } + return YES; +} + +- (NSData*)readData:(NSError**)error { + NSMutableData* encodedData; + if (_finished) { + encodedData = [[NSMutableData alloc] init]; + } else { + encodedData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize]; + if (encodedData == nil) { + GWS_DNOT_REACHED(); + return nil; + } + NSUInteger length = 0; + do { + NSData* data = [super readData:error]; + if (data == nil) { + return nil; + } + _stream.next_in = (Bytef*)data.bytes; + _stream.avail_in = (uInt)data.length; + while (1) { + NSUInteger maxLength = encodedData.length - length; + _stream.next_out = (Bytef*)((char*)encodedData.mutableBytes + length); + _stream.avail_out = (uInt)maxLength; + int result = deflate(&_stream, data.length ? Z_NO_FLUSH : Z_FINISH); + if (result == Z_STREAM_END) { + _finished = YES; + } else if (result != Z_OK) { + if (error) { + *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; + } + return nil; + } + length += maxLength - _stream.avail_out; + if (_stream.avail_out > 0) { + break; + } + encodedData.length = 2 * encodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available + } + GWS_DCHECK(_stream.avail_in == 0); + } while (length == 0); // Make sure we don't return an empty NSData if not in finished state + encodedData.length = length; + } + return encodedData; +} + +- (void)close { + deflateEnd(&_stream); + [super close]; +} + +@end + +@implementation GCDWebServerResponse { + BOOL _opened; + NSMutableArray* _encoders; + id __unsafe_unretained _reader; +} + ++ (instancetype)response { + return [[[self class] alloc] init]; +} + +- (instancetype)init { + if ((self = [super init])) { + _contentType = nil; + _contentLength = NSUIntegerMax; + _statusCode = kGCDWebServerHTTPStatusCode_OK; + _cacheControlMaxAge = 0; + _additionalHeaders = [[NSMutableDictionary alloc] init]; + _encoders = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header { + [_additionalHeaders setValue:value forKey:header]; +} + +- (BOOL)hasBody { + return _contentType ? YES : NO; +} + +- (BOOL)usesChunkedTransferEncoding { + return (_contentType != nil) && (_contentLength == NSUIntegerMax); +} + +- (BOOL)open:(NSError**)error { + return YES; +} + +- (NSData*)readData:(NSError**)error { + return [NSData data]; +} + +- (void)close { + ; +} + +- (void)prepareForReading { + _reader = self; + if (_gzipContentEncodingEnabled) { + GCDWebServerGZipEncoder* encoder = [[GCDWebServerGZipEncoder alloc] initWithResponse:self reader:_reader]; + [_encoders addObject:encoder]; + _reader = encoder; + } +} + +- (BOOL)performOpen:(NSError**)error { + GWS_DCHECK(_contentType); + GWS_DCHECK(_reader); + if (_opened) { + GWS_DNOT_REACHED(); + return NO; + } + _opened = YES; + return [_reader open:error]; +} + +- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block { + GWS_DCHECK(_opened); + if ([_reader respondsToSelector:@selector(asyncReadDataWithCompletion:)]) { + [_reader asyncReadDataWithCompletion:[block copy]]; + } else { + NSError* error = nil; + NSData* data = [_reader readData:&error]; + block(data, error); + } +} + +- (void)performClose { + GWS_DCHECK(_opened); + [_reader close]; +} + +- (NSString*)description { + NSMutableString* description = [NSMutableString stringWithFormat:@"Status Code = %i", (int)_statusCode]; + if (_contentType) { + [description appendFormat:@"\nContent Type = %@", _contentType]; + } + if (_contentLength != NSUIntegerMax) { + [description appendFormat:@"\nContent Length = %lu", (unsigned long)_contentLength]; + } + [description appendFormat:@"\nCache Control Max Age = %lu", (unsigned long)_cacheControlMaxAge]; + if (_lastModifiedDate) { + [description appendFormat:@"\nLast Modified Date = %@", _lastModifiedDate]; + } + if (_eTag) { + [description appendFormat:@"\nETag = %@", _eTag]; + } + if (_additionalHeaders.count) { + [description appendString:@"\n"]; + for (NSString* header in [[_additionalHeaders allKeys] sortedArrayUsingSelector:@selector(compare:)]) { + [description appendFormat:@"\n%@: %@", header, [_additionalHeaders objectForKey:header]]; + } + } + return description; +} + +@end + +@implementation GCDWebServerResponse (Extensions) + ++ (instancetype)responseWithStatusCode:(NSInteger)statusCode { + return [[self alloc] initWithStatusCode:statusCode]; +} + ++ (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent { + return [[self alloc] initWithRedirect:location permanent:permanent]; +} + +- (instancetype)initWithStatusCode:(NSInteger)statusCode { + if ((self = [self init])) { + self.statusCode = statusCode; + } + return self; +} + +- (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent { + if ((self = [self init])) { + self.statusCode = permanent ? kGCDWebServerHTTPStatusCode_MovedPermanently : kGCDWebServerHTTPStatusCode_TemporaryRedirect; + [self setValue:[location absoluteString] forAdditionalHeader:@"Location"]; + } + return self; +} + +@end diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerDataRequest.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerDataRequest.h new file mode 100644 index 0000000..f21a4b7 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerDataRequest.h @@ -0,0 +1,64 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GCDWebServerRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * The GCDWebServerDataRequest subclass of GCDWebServerRequest stores the body + * of the HTTP request in memory. + */ +@interface GCDWebServerDataRequest : GCDWebServerRequest + +/** + * Returns the data for the request body. + */ +@property(nonatomic, readonly) NSData* data; + +@end + +@interface GCDWebServerDataRequest (Extensions) + +/** + * Returns the data for the request body interpreted as text. If the content + * type of the body is not a text one, or if an error occurs, nil is returned. + * + * The text encoding used to interpret the data is extracted from the + * "Content-Type" header or defaults to UTF-8. + */ +@property(nonatomic, readonly, nullable) NSString* text; + +/** + * Returns the data for the request body interpreted as a JSON object. If the + * content type of the body is not JSON, or if an error occurs, nil is returned. + */ +@property(nonatomic, readonly, nullable) id jsonObject; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerDataRequest.m b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerDataRequest.m new file mode 100644 index 0000000..3ea9bba --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerDataRequest.m @@ -0,0 +1,104 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error GCDWebServer requires ARC +#endif + +#import "GCDWebServerPrivate.h" + +@interface GCDWebServerDataRequest () +@property(nonatomic) NSMutableData* data; +@end + +@implementation GCDWebServerDataRequest { + NSString* _text; + id _jsonObject; +} + +- (BOOL)open:(NSError**)error { + if (self.contentLength != NSUIntegerMax) { + _data = [[NSMutableData alloc] initWithCapacity:self.contentLength]; + } else { + _data = [[NSMutableData alloc] init]; + } + if (_data == nil) { + if (error) { + *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed allocating memory" }]; + } + return NO; + } + return YES; +} + +- (BOOL)writeData:(NSData*)data error:(NSError**)error { + [_data appendData:data]; + return YES; +} + +- (BOOL)close:(NSError**)error { + return YES; +} + +- (NSString*)description { + NSMutableString* description = [NSMutableString stringWithString:[super description]]; + if (_data) { + [description appendString:@"\n\n"]; + [description appendString:GCDWebServerDescribeData(_data, (NSString*)self.contentType)]; + } + return description; +} + +@end + +@implementation GCDWebServerDataRequest (Extensions) + +- (NSString*)text { + if (_text == nil) { + if ([self.contentType hasPrefix:@"text/"]) { + NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); + _text = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)]; + } else { + GWS_DNOT_REACHED(); + } + } + return _text; +} + +- (id)jsonObject { + if (_jsonObject == nil) { + NSString* mimeType = GCDWebServerTruncateHeaderValue(self.contentType); + if ([mimeType isEqualToString:@"application/json"] || [mimeType isEqualToString:@"text/json"] || [mimeType isEqualToString:@"text/javascript"]) { + _jsonObject = [NSJSONSerialization JSONObjectWithData:_data options:0 error:NULL]; + } else { + GWS_DNOT_REACHED(); + } + } + return _jsonObject; +} + +@end diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerFileRequest.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerFileRequest.h new file mode 100644 index 0000000..8aceae4 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerFileRequest.h @@ -0,0 +1,49 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GCDWebServerRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * The GCDWebServerFileRequest subclass of GCDWebServerRequest stores the body + * of the HTTP request to a file on disk. + */ +@interface GCDWebServerFileRequest : GCDWebServerRequest + +/** + * Returns the path to the temporary file containing the request body. + * + * @warning This temporary file will be automatically deleted when the + * GCDWebServerFileRequest is deallocated. If you want to preserve this file, + * you must move it to a different location beforehand. + */ +@property(nonatomic, readonly) NSString* temporaryPath; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerFileRequest.m b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerFileRequest.m new file mode 100644 index 0000000..8a47fcc --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerFileRequest.m @@ -0,0 +1,102 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error GCDWebServer requires ARC +#endif + +#import "GCDWebServerPrivate.h" + +@implementation GCDWebServerFileRequest { + int _file; +} + +- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { + if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) { + _temporaryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; + } + return self; +} + +- (void)dealloc { + unlink([_temporaryPath fileSystemRepresentation]); +} + +- (BOOL)open:(NSError**)error { + _file = open([_temporaryPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (_file <= 0) { + if (error) { + *error = GCDWebServerMakePosixError(errno); + } + return NO; + } + return YES; +} + +- (BOOL)writeData:(NSData*)data error:(NSError**)error { + if (write(_file, data.bytes, data.length) != (ssize_t)data.length) { + if (error) { + *error = GCDWebServerMakePosixError(errno); + } + return NO; + } + return YES; +} + +- (BOOL)close:(NSError**)error { + if (close(_file) < 0) { + if (error) { + *error = GCDWebServerMakePosixError(errno); + } + return NO; + } +#ifdef __GCDWEBSERVER_ENABLE_TESTING__ + NSString* creationDateHeader = [self.headers objectForKey:@"X-GCDWebServer-CreationDate"]; + if (creationDateHeader) { + NSDate* date = GCDWebServerParseISO8601(creationDateHeader); + if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileCreationDate : date} ofItemAtPath:_temporaryPath error:error]) { + return NO; + } + } + NSString* modifiedDateHeader = [self.headers objectForKey:@"X-GCDWebServer-ModifiedDate"]; + if (modifiedDateHeader) { + NSDate* date = GCDWebServerParseRFC822(modifiedDateHeader); + if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileModificationDate : date} ofItemAtPath:_temporaryPath error:error]) { + return NO; + } + } +#endif + return YES; +} + +- (NSString*)description { + NSMutableString* description = [NSMutableString stringWithString:[super description]]; + [description appendFormat:@"\n\n{%@}", _temporaryPath]; + return description; +} + +@end diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.h new file mode 100644 index 0000000..93ac179 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.h @@ -0,0 +1,136 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GCDWebServerRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * The GCDWebServerMultiPart class is an abstract class that wraps the content + * of a part. + */ +@interface GCDWebServerMultiPart : NSObject + +/** + * Returns the control name retrieved from the part headers. + */ +@property(nonatomic, readonly) NSString* controlName; + +/** + * Returns the content type retrieved from the part headers or "text/plain" + * if not available (per HTTP specifications). + */ +@property(nonatomic, readonly) NSString* contentType; + +/** + * Returns the MIME type component of the content type for the part. + */ +@property(nonatomic, readonly) NSString* mimeType; + +@end + +/** + * The GCDWebServerMultiPartArgument subclass of GCDWebServerMultiPart wraps + * the content of a part as data in memory. + */ +@interface GCDWebServerMultiPartArgument : GCDWebServerMultiPart + +/** + * Returns the data for the part. + */ +@property(nonatomic, readonly) NSData* data; + +/** + * Returns the data for the part interpreted as text. If the content + * type of the part is not a text one, or if an error occurs, nil is returned. + * + * The text encoding used to interpret the data is extracted from the + * "Content-Type" header or defaults to UTF-8. + */ +@property(nonatomic, readonly, nullable) NSString* string; + +@end + +/** + * The GCDWebServerMultiPartFile subclass of GCDWebServerMultiPart wraps + * the content of a part as a file on disk. + */ +@interface GCDWebServerMultiPartFile : GCDWebServerMultiPart + +/** + * Returns the file name retrieved from the part headers. + */ +@property(nonatomic, readonly) NSString* fileName; + +/** + * Returns the path to the temporary file containing the part data. + * + * @warning This temporary file will be automatically deleted when the + * GCDWebServerMultiPartFile is deallocated. If you want to preserve this file, + * you must move it to a different location beforehand. + */ +@property(nonatomic, readonly) NSString* temporaryPath; + +@end + +/** + * The GCDWebServerMultiPartFormRequest subclass of GCDWebServerRequest + * parses the body of the HTTP request as a multipart encoded form. + */ +@interface GCDWebServerMultiPartFormRequest : GCDWebServerRequest + +/** + * Returns the argument parts from the multipart encoded form as + * name / GCDWebServerMultiPartArgument pairs. + */ +@property(nonatomic, readonly) NSArray* arguments; + +/** + * Returns the files parts from the multipart encoded form as + * name / GCDWebServerMultiPartFile pairs. + */ +@property(nonatomic, readonly) NSArray* files; + +/** + * Returns the MIME type for multipart encoded forms + * i.e. "multipart/form-data". + */ ++ (NSString*)mimeType; + +/** + * Returns the first argument for a given control name or nil if not found. + */ +- (nullable GCDWebServerMultiPartArgument*)firstArgumentForControlName:(NSString*)name; + +/** + * Returns the first file for a given control name or nil if not found. + */ +- (nullable GCDWebServerMultiPartFile*)firstFileForControlName:(NSString*)name; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.m b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.m new file mode 100644 index 0000000..4e6bf09 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.m @@ -0,0 +1,405 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error GCDWebServer requires ARC +#endif + +#import "GCDWebServerPrivate.h" + +#define kMultiPartBufferSize (256 * 1024) + +typedef enum { + kParserState_Undefined = 0, + kParserState_Start, + kParserState_Headers, + kParserState_Content, + kParserState_End +} ParserState; + +@interface GCDWebServerMIMEStreamParser : NSObject +@end + +static NSData* _newlineData = nil; +static NSData* _newlinesData = nil; +static NSData* _dashNewlineData = nil; + +@implementation GCDWebServerMultiPart + +- (instancetype)initWithControlName:(NSString* _Nonnull)name contentType:(NSString* _Nonnull)type { + if ((self = [super init])) { + _controlName = [name copy]; + _contentType = [type copy]; + _mimeType = (NSString*)GCDWebServerTruncateHeaderValue(_contentType); + } + return self; +} + +@end + +@implementation GCDWebServerMultiPartArgument + +- (instancetype)initWithControlName:(NSString* _Nonnull)name contentType:(NSString* _Nonnull)type data:(NSData* _Nonnull)data { + if ((self = [super initWithControlName:name contentType:type])) { + _data = data; + + if ([self.contentType hasPrefix:@"text/"]) { + NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); + _string = [[NSString alloc] initWithData:_data encoding:GCDWebServerStringEncodingFromCharset(charset)]; + } + } + return self; +} + +- (NSString*)description { + return [NSString stringWithFormat:@"<%@ | '%@' | %lu bytes>", [self class], self.mimeType, (unsigned long)_data.length]; +} + +@end + +@implementation GCDWebServerMultiPartFile + +- (instancetype)initWithControlName:(NSString* _Nonnull)name contentType:(NSString* _Nonnull)type fileName:(NSString* _Nonnull)fileName temporaryPath:(NSString* _Nonnull)temporaryPath { + if ((self = [super initWithControlName:name contentType:type])) { + _fileName = [fileName copy]; + _temporaryPath = [temporaryPath copy]; + } + return self; +} + +- (void)dealloc { + unlink([_temporaryPath fileSystemRepresentation]); +} + +- (NSString*)description { + return [NSString stringWithFormat:@"<%@ | '%@' | '%@>'", [self class], self.mimeType, _fileName]; +} + +@end + +@implementation GCDWebServerMIMEStreamParser { + NSData* _boundary; + NSString* _defaultcontrolName; + ParserState _state; + NSMutableData* _data; + NSMutableArray* _arguments; + NSMutableArray* _files; + + NSString* _controlName; + NSString* _fileName; + NSString* _contentType; + NSString* _tmpPath; + int _tmpFile; + GCDWebServerMIMEStreamParser* _subParser; +} + ++ (void)initialize { + if (_newlineData == nil) { + _newlineData = [[NSData alloc] initWithBytes:"\r\n" length:2]; + GWS_DCHECK(_newlineData); + } + if (_newlinesData == nil) { + _newlinesData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; + GWS_DCHECK(_newlinesData); + } + if (_dashNewlineData == nil) { + _dashNewlineData = [[NSData alloc] initWithBytes:"--\r\n" length:4]; + GWS_DCHECK(_dashNewlineData); + } +} + +- (instancetype)initWithBoundary:(NSString* _Nonnull)boundary defaultControlName:(NSString* _Nullable)name arguments:(NSMutableArray* _Nonnull)arguments files:(NSMutableArray* _Nonnull)files { + NSData* data = boundary.length ? [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding] : nil; + if (data == nil) { + GWS_DNOT_REACHED(); + return nil; + } + if ((self = [super init])) { + _boundary = data; + _defaultcontrolName = name; + _arguments = arguments; + _files = files; + _data = [[NSMutableData alloc] initWithCapacity:kMultiPartBufferSize]; + _state = kParserState_Start; + } + return self; +} + +- (void)dealloc { + if (_tmpFile > 0) { + close(_tmpFile); + unlink([_tmpPath fileSystemRepresentation]); + } +} + +// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 +- (BOOL)_parseData { + BOOL success = YES; + + if (_state == kParserState_Headers) { + NSRange range = [_data rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _data.length)]; + if (range.location != NSNotFound) { + _controlName = nil; + _fileName = nil; + _contentType = nil; + _tmpPath = nil; + _subParser = nil; + NSString* headers = [[NSString alloc] initWithData:[_data subdataWithRange:NSMakeRange(0, range.location)] encoding:NSUTF8StringEncoding]; + if (headers) { + for (NSString* header in [headers componentsSeparatedByString:@"\r\n"]) { + NSRange subRange = [header rangeOfString:@":"]; + if (subRange.location != NSNotFound) { + NSString* name = [header substringToIndex:subRange.location]; + NSString* value = [[header substringFromIndex:(subRange.location + subRange.length)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + if ([name caseInsensitiveCompare:@"Content-Type"] == NSOrderedSame) { + _contentType = GCDWebServerNormalizeHeaderValue(value); + } else if ([name caseInsensitiveCompare:@"Content-Disposition"] == NSOrderedSame) { + NSString* contentDisposition = GCDWebServerNormalizeHeaderValue(value); + if ([GCDWebServerTruncateHeaderValue(contentDisposition) isEqualToString:@"form-data"]) { + _controlName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"name"); + _fileName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename"); + } else if ([GCDWebServerTruncateHeaderValue(contentDisposition) isEqualToString:@"file"]) { + _controlName = _defaultcontrolName; + _fileName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename"); + } + } + } else { + GWS_DNOT_REACHED(); + } + } + if (_contentType == nil) { + _contentType = @"text/plain"; + } + } else { + GWS_LOG_ERROR(@"Failed decoding headers in part of 'multipart/form-data'"); + GWS_DNOT_REACHED(); + } + if (_controlName) { + if ([GCDWebServerTruncateHeaderValue(_contentType) isEqualToString:@"multipart/mixed"]) { + NSString* boundary = GCDWebServerExtractHeaderValueParameter(_contentType, @"boundary"); + _subParser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:_controlName arguments:_arguments files:_files]; + if (_subParser == nil) { + GWS_DNOT_REACHED(); + success = NO; + } + } else if (_fileName) { + NSString* path = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; + _tmpFile = open([path fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (_tmpFile > 0) { + _tmpPath = [path copy]; + } else { + GWS_DNOT_REACHED(); + success = NO; + } + } + } else { + GWS_DNOT_REACHED(); + success = NO; + } + + [_data replaceBytesInRange:NSMakeRange(0, range.location + range.length) withBytes:NULL length:0]; + _state = kParserState_Content; + } + } + + if ((_state == kParserState_Start) || (_state == kParserState_Content)) { + NSRange range = [_data rangeOfData:_boundary options:0 range:NSMakeRange(0, _data.length)]; + if (range.location != NSNotFound) { + NSRange subRange = NSMakeRange(range.location + range.length, _data.length - range.location - range.length); + NSRange subRange1 = [_data rangeOfData:_newlineData options:NSDataSearchAnchored range:subRange]; + NSRange subRange2 = [_data rangeOfData:_dashNewlineData options:NSDataSearchAnchored range:subRange]; + if ((subRange1.location != NSNotFound) || (subRange2.location != NSNotFound)) { + if (_state == kParserState_Content) { + const void* dataBytes = _data.bytes; + NSUInteger dataLength = range.location - 2; + if (_subParser) { + if (![_subParser appendBytes:dataBytes length:(dataLength + 2)] || ![_subParser isAtEnd]) { + GWS_DNOT_REACHED(); + success = NO; + } + _subParser = nil; + } else if (_tmpPath) { + ssize_t result = write(_tmpFile, dataBytes, dataLength); + if (result == (ssize_t)dataLength) { + if (close(_tmpFile) == 0) { + _tmpFile = 0; + GCDWebServerMultiPartFile* file = [[GCDWebServerMultiPartFile alloc] initWithControlName:_controlName contentType:_contentType fileName:_fileName temporaryPath:_tmpPath]; + [_files addObject:file]; + } else { + GWS_DNOT_REACHED(); + success = NO; + } + } else { + GWS_DNOT_REACHED(); + success = NO; + } + _tmpPath = nil; + } else { + NSData* data = [[NSData alloc] initWithBytes:(void*)dataBytes length:dataLength]; + GCDWebServerMultiPartArgument* argument = [[GCDWebServerMultiPartArgument alloc] initWithControlName:_controlName contentType:_contentType data:data]; + [_arguments addObject:argument]; + } + } + + if (subRange1.location != NSNotFound) { + [_data replaceBytesInRange:NSMakeRange(0, subRange1.location + subRange1.length) withBytes:NULL length:0]; + _state = kParserState_Headers; + success = [self _parseData]; + } else { + _state = kParserState_End; + } + } + } else { + NSUInteger margin = 2 * _boundary.length; + if (_data.length > margin) { + NSUInteger length = _data.length - margin; + if (_subParser) { + if ([_subParser appendBytes:_data.bytes length:length]) { + [_data replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0]; + } else { + GWS_DNOT_REACHED(); + success = NO; + } + } else if (_tmpPath) { + ssize_t result = write(_tmpFile, _data.bytes, length); + if (result == (ssize_t)length) { + [_data replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0]; + } else { + GWS_DNOT_REACHED(); + success = NO; + } + } + } + } + } + + return success; +} + +- (BOOL)appendBytes:(const void*)bytes length:(NSUInteger)length { + [_data appendBytes:bytes length:length]; + return [self _parseData]; +} + +- (BOOL)isAtEnd { + return (_state == kParserState_End); +} + +@end + +@interface GCDWebServerMultiPartFormRequest () +@property(nonatomic) NSMutableArray* arguments; +@property(nonatomic) NSMutableArray* files; +@end + +@implementation GCDWebServerMultiPartFormRequest { + GCDWebServerMIMEStreamParser* _parser; +} + ++ (NSString*)mimeType { + return @"multipart/form-data"; +} + +- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { + if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) { + _arguments = [[NSMutableArray alloc] init]; + _files = [[NSMutableArray alloc] init]; + } + return self; +} + +- (BOOL)open:(NSError**)error { + NSString* boundary = GCDWebServerExtractHeaderValueParameter(self.contentType, @"boundary"); + _parser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:nil arguments:_arguments files:_files]; + if (_parser == nil) { + if (error) { + *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed starting to parse multipart form data" }]; + } + return NO; + } + return YES; +} + +- (BOOL)writeData:(NSData*)data error:(NSError**)error { + if (![_parser appendBytes:data.bytes length:data.length]) { + if (error) { + *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed continuing to parse multipart form data" }]; + } + return NO; + } + return YES; +} + +- (BOOL)close:(NSError**)error { + BOOL atEnd = [_parser isAtEnd]; + _parser = nil; + if (!atEnd) { + if (error) { + *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed finishing to parse multipart form data" }]; + } + return NO; + } + return YES; +} + +- (GCDWebServerMultiPartArgument*)firstArgumentForControlName:(NSString*)name { + for (GCDWebServerMultiPartArgument* argument in _arguments) { + if ([argument.controlName isEqualToString:name]) { + return argument; + } + } + return nil; +} + +- (GCDWebServerMultiPartFile*)firstFileForControlName:(NSString*)name { + for (GCDWebServerMultiPartFile* file in _files) { + if ([file.controlName isEqualToString:name]) { + return file; + } + } + return nil; +} + +- (NSString*)description { + NSMutableString* description = [NSMutableString stringWithString:[super description]]; + if (_arguments.count) { + [description appendString:@"\n"]; + for (GCDWebServerMultiPartArgument* argument in _arguments) { + [description appendFormat:@"\n%@ (%@)\n", argument.controlName, argument.contentType]; + [description appendString:GCDWebServerDescribeData(argument.data, argument.contentType)]; + } + } + if (_files.count) { + [description appendString:@"\n"]; + for (GCDWebServerMultiPartFile* file in _files) { + [description appendFormat:@"\n%@ (%@): %@\n{%@}", file.controlName, file.contentType, file.fileName, file.temporaryPath]; + } + } + return description; +} + +@end diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.h new file mode 100644 index 0000000..fcf177e --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.h @@ -0,0 +1,55 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GCDWebServerDataRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * The GCDWebServerURLEncodedFormRequest subclass of GCDWebServerRequest + * parses the body of the HTTP request as a URL encoded form using + * GCDWebServerParseURLEncodedForm(). + */ +@interface GCDWebServerURLEncodedFormRequest : GCDWebServerDataRequest + +/** + * Returns the unescaped control names and values for the URL encoded form. + * + * The text encoding used to interpret the data is extracted from the + * "Content-Type" header or defaults to UTF-8. + */ +@property(nonatomic, readonly) NSDictionary* arguments; + +/** + * Returns the MIME type for URL encoded forms + * i.e. "application/x-www-form-urlencoded". + */ ++ (NSString*)mimeType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.m b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.m new file mode 100644 index 0000000..7e0137f --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.m @@ -0,0 +1,60 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error GCDWebServer requires ARC +#endif + +#import "GCDWebServerPrivate.h" + +@implementation GCDWebServerURLEncodedFormRequest + ++ (NSString*)mimeType { + return @"application/x-www-form-urlencoded"; +} + +- (BOOL)close:(NSError**)error { + if (![super close:error]) { + return NO; + } + + NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); + NSString* string = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)]; + _arguments = GCDWebServerParseURLEncodedForm(string); + return YES; +} + +- (NSString*)description { + NSMutableString* description = [NSMutableString stringWithString:[super description]]; + [description appendString:@"\n"]; + for (NSString* argument in [[_arguments allKeys] sortedArrayUsingSelector:@selector(compare:)]) { + [description appendFormat:@"\n%@ = %@", argument, [_arguments objectForKey:argument]]; + } + return description; +} + +@end diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerDataResponse.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerDataResponse.h new file mode 100644 index 0000000..783f596 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerDataResponse.h @@ -0,0 +1,113 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GCDWebServerResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * The GCDWebServerDataResponse subclass of GCDWebServerResponse reads the body + * of the HTTP response from memory. + */ +@interface GCDWebServerDataResponse : GCDWebServerResponse +@property(nonatomic, copy) NSString* contentType; // Redeclare as non-null + +/** + * Creates a response with data in memory and a given content type. + */ ++ (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type; + +/** + * This method is the designated initializer for the class. + */ +- (instancetype)initWithData:(NSData*)data contentType:(NSString*)type; + +@end + +@interface GCDWebServerDataResponse (Extensions) + +/** + * Creates a data response from text encoded using UTF-8. + */ ++ (nullable instancetype)responseWithText:(NSString*)text; + +/** + * Creates a data response from HTML encoded using UTF-8. + */ ++ (nullable instancetype)responseWithHTML:(NSString*)html; + +/** + * Creates a data response from an HTML template encoded using UTF-8. + * See -initWithHTMLTemplate:variables: for details. + */ ++ (nullable instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; + +/** + * Creates a data response from a serialized JSON object and the default + * "application/json" content type. + */ ++ (nullable instancetype)responseWithJSONObject:(id)object; + +/** + * Creates a data response from a serialized JSON object and a custom + * content type. + */ ++ (nullable instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type; + +/** + * Initializes a data response from text encoded using UTF-8. + */ +- (nullable instancetype)initWithText:(NSString*)text; + +/** + * Initializes a data response from HTML encoded using UTF-8. + */ +- (nullable instancetype)initWithHTML:(NSString*)html; + +/** + * Initializes a data response from an HTML template encoded using UTF-8. + * + * All occurences of "%variable%" within the HTML template are replaced with + * their corresponding values. + */ +- (nullable instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; + +/** + * Initializes a data response from a serialized JSON object and the default + * "application/json" content type. + */ +- (nullable instancetype)initWithJSONObject:(id)object; + +/** + * Initializes a data response from a serialized JSON object and a custom + * content type. + */ +- (nullable instancetype)initWithJSONObject:(id)object contentType:(NSString*)type; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerDataResponse.m b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerDataResponse.m new file mode 100644 index 0000000..b496847 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerDataResponse.m @@ -0,0 +1,136 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error GCDWebServer requires ARC +#endif + +#import "GCDWebServerPrivate.h" + +@implementation GCDWebServerDataResponse { + NSData* _data; + BOOL _done; +} + +@dynamic contentType; + ++ (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type { + return [[[self class] alloc] initWithData:data contentType:type]; +} + +- (instancetype)initWithData:(NSData*)data contentType:(NSString*)type { + if ((self = [super init])) { + _data = data; + + self.contentType = type; + self.contentLength = data.length; + } + return self; +} + +- (NSData*)readData:(NSError**)error { + NSData* data; + if (_done) { + data = [NSData data]; + } else { + data = _data; + _done = YES; + } + return data; +} + +- (NSString*)description { + NSMutableString* description = [NSMutableString stringWithString:[super description]]; + [description appendString:@"\n\n"]; + [description appendString:GCDWebServerDescribeData(_data, self.contentType)]; + return description; +} + +@end + +@implementation GCDWebServerDataResponse (Extensions) + ++ (instancetype)responseWithText:(NSString*)text { + return [[self alloc] initWithText:text]; +} + ++ (instancetype)responseWithHTML:(NSString*)html { + return [[self alloc] initWithHTML:html]; +} + ++ (instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables { + return [[self alloc] initWithHTMLTemplate:path variables:variables]; +} + ++ (instancetype)responseWithJSONObject:(id)object { + return [[self alloc] initWithJSONObject:object]; +} + ++ (instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type { + return [[self alloc] initWithJSONObject:object contentType:type]; +} + +- (instancetype)initWithText:(NSString*)text { + NSData* data = [text dataUsingEncoding:NSUTF8StringEncoding]; + if (data == nil) { + GWS_DNOT_REACHED(); + return nil; + } + return [self initWithData:data contentType:@"text/plain; charset=utf-8"]; +} + +- (instancetype)initWithHTML:(NSString*)html { + NSData* data = [html dataUsingEncoding:NSUTF8StringEncoding]; + if (data == nil) { + GWS_DNOT_REACHED(); + return nil; + } + return [self initWithData:data contentType:@"text/html; charset=utf-8"]; +} + +- (instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables { + NSMutableString* html = [[NSMutableString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; + [variables enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL* stop) { + [html replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, html.length)]; + }]; + return [self initWithHTML:html]; +} + +- (instancetype)initWithJSONObject:(id)object { + return [self initWithJSONObject:object contentType:@"application/json"]; +} + +- (instancetype)initWithJSONObject:(id)object contentType:(NSString*)type { + NSData* data = [NSJSONSerialization dataWithJSONObject:object options:0 error:NULL]; + if (data == nil) { + GWS_DNOT_REACHED(); + return nil; + } + return [self initWithData:data contentType:type]; +} + +@end diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerErrorResponse.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerErrorResponse.h new file mode 100644 index 0000000..92c834c --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerErrorResponse.h @@ -0,0 +1,85 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GCDWebServerDataResponse.h" +#import "GCDWebServerHTTPStatusCodes.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * The GCDWebServerDataResponse subclass of GCDWebServerDataResponse generates + * an HTML body from an HTTP status code and an error message. + */ +@interface GCDWebServerErrorResponse : GCDWebServerDataResponse + +/** + * Creates a client error response with the corresponding HTTP status code. + */ ++ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); + +/** + * Creates a server error response with the corresponding HTTP status code. + */ ++ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); + +/** + * Creates a client error response with the corresponding HTTP status code + * and an underlying NSError. + */ ++ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); + +/** + * Creates a server error response with the corresponding HTTP status code + * and an underlying NSError. + */ ++ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); + +/** + * Initializes a client error response with the corresponding HTTP status code. + */ +- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); + +/** + * Initializes a server error response with the corresponding HTTP status code. + */ +- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); + +/** + * Initializes a client error response with the corresponding HTTP status code + * and an underlying NSError. + */ +- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); + +/** + * Initializes a server error response with the corresponding HTTP status code + * and an underlying NSError. + */ +- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerErrorResponse.m b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerErrorResponse.m new file mode 100644 index 0000000..f1cd202 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerErrorResponse.m @@ -0,0 +1,124 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error GCDWebServer requires ARC +#endif + +#import "GCDWebServerPrivate.h" + +@implementation GCDWebServerErrorResponse + ++ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { + GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); + va_list arguments; + va_start(arguments, format); + GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; + va_end(arguments); + return response; +} + ++ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { + GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); + va_list arguments; + va_start(arguments, format); + GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; + va_end(arguments); + return response; +} + ++ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { + GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); + va_list arguments; + va_start(arguments, format); + GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; + va_end(arguments); + return response; +} + ++ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { + GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); + va_list arguments; + va_start(arguments, format); + GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; + va_end(arguments); + return response; +} + +static inline NSString* _EscapeHTMLString(NSString* string) { + return [string stringByReplacingOccurrencesOfString:@"\"" withString:@"""]; +} + +- (instancetype)initWithStatusCode:(NSInteger)statusCode underlyingError:(NSError*)underlyingError messageFormat:(NSString*)format arguments:(va_list)arguments { + NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments]; + NSString* title = [NSString stringWithFormat:@"HTTP Error %i", (int)statusCode]; + NSString* error = underlyingError ? [NSString stringWithFormat:@"[%@] %@ (%li)", underlyingError.domain, _EscapeHTMLString(underlyingError.localizedDescription), (long)underlyingError.code] : @""; + NSString* html = [NSString stringWithFormat:@"%@

%@: %@

%@

", + title, title, _EscapeHTMLString(message), error]; + if ((self = [self initWithHTML:html])) { + self.statusCode = statusCode; + } + return self; +} + +- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { + GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); + va_list arguments; + va_start(arguments, format); + self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; + va_end(arguments); + return self; +} + +- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { + GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); + va_list arguments; + va_start(arguments, format); + self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; + va_end(arguments); + return self; +} + +- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { + GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); + va_list arguments; + va_start(arguments, format); + self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; + va_end(arguments); + return self; +} + +- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { + GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); + va_list arguments; + va_start(arguments, format); + self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; + va_end(arguments); + return self; +} + +@end diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerFileResponse.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerFileResponse.h new file mode 100644 index 0000000..9403835 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerFileResponse.h @@ -0,0 +1,108 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GCDWebServerResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * The GCDWebServerFileResponse subclass of GCDWebServerResponse reads the body + * of the HTTP response from a file on disk. + * + * It will automatically set the contentType, lastModifiedDate and eTag + * properties of the GCDWebServerResponse according to the file extension and + * metadata. + */ +@interface GCDWebServerFileResponse : GCDWebServerResponse +@property(nonatomic, copy) NSString* contentType; // Redeclare as non-null +@property(nonatomic) NSDate* lastModifiedDate; // Redeclare as non-null +@property(nonatomic, copy) NSString* eTag; // Redeclare as non-null + +/** + * Creates a response with the contents of a file. + */ ++ (nullable instancetype)responseWithFile:(NSString*)path; + +/** + * Creates a response like +responseWithFile: and sets the "Content-Disposition" + * HTTP header for a download if the "attachment" argument is YES. + */ ++ (nullable instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment; + +/** + * Creates a response like +responseWithFile: but restricts the file contents + * to a specific byte range. + * + * See -initWithFile:byteRange: for details. + */ ++ (nullable instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range; + +/** + * Creates a response like +responseWithFile:byteRange: and sets the + * "Content-Disposition" HTTP header for a download if the "attachment" + * argument is YES. + */ ++ (nullable instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment; + +/** + * Initializes a response with the contents of a file. + */ +- (nullable instancetype)initWithFile:(NSString*)path; + +/** + * Initializes a response like +responseWithFile: and sets the + * "Content-Disposition" HTTP header for a download if the "attachment" + * argument is YES. + */ +- (nullable instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment; + +/** + * Initializes a response like -initWithFile: but restricts the file contents + * to a specific byte range. This range should be set to (NSUIntegerMax, 0) for + * the full file, (offset, length) if expressed from the beginning of the file, + * or (NSUIntegerMax, length) if expressed from the end of the file. The "offset" + * and "length" values will be automatically adjusted to be compatible with the + * actual size of the file. + * + * This argument would typically be set to the value of the byteRange property + * of the current GCDWebServerRequest. + */ +- (nullable instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range; + +/** + * This method is the designated initializer for the class. + * + * If MIME type overrides are specified, they allow to customize the built-in + * mapping from extensions to MIME types. Keys of the dictionary must be lowercased + * file extensions without the period, and the values must be the corresponding + * MIME types. + */ +- (nullable instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment mimeTypeOverrides:(nullable NSDictionary*)overrides; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerFileResponse.m b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerFileResponse.m new file mode 100644 index 0000000..bd07518 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerFileResponse.m @@ -0,0 +1,185 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error GCDWebServer requires ARC +#endif + +#import + +#import "GCDWebServerPrivate.h" + +#define kFileReadBufferSize (32 * 1024) + +@implementation GCDWebServerFileResponse { + NSString* _path; + NSUInteger _offset; + NSUInteger _size; + int _file; +} + +@dynamic contentType, lastModifiedDate, eTag; + ++ (instancetype)responseWithFile:(NSString*)path { + return [[[self class] alloc] initWithFile:path]; +} + ++ (instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment { + return [[[self class] alloc] initWithFile:path isAttachment:attachment]; +} + ++ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range { + return [[[self class] alloc] initWithFile:path byteRange:range]; +} + ++ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment { + return [[[self class] alloc] initWithFile:path byteRange:range isAttachment:attachment mimeTypeOverrides:nil]; +} + +- (instancetype)initWithFile:(NSString*)path { + return [self initWithFile:path byteRange:NSMakeRange(NSUIntegerMax, 0) isAttachment:NO mimeTypeOverrides:nil]; +} + +- (instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment { + return [self initWithFile:path byteRange:NSMakeRange(NSUIntegerMax, 0) isAttachment:attachment mimeTypeOverrides:nil]; +} + +- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range { + return [self initWithFile:path byteRange:range isAttachment:NO mimeTypeOverrides:nil]; +} + +static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) { + return [NSDate dateWithTimeIntervalSince1970:((NSTimeInterval)t->tv_sec + (NSTimeInterval)t->tv_nsec / 1000000000.0)]; +} + +- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment mimeTypeOverrides:(NSDictionary*)overrides { + struct stat info; + if (lstat([path fileSystemRepresentation], &info) || !(info.st_mode & S_IFREG)) { + GWS_DNOT_REACHED(); + return nil; + } +#ifndef __LP64__ + if (info.st_size >= (off_t)4294967295) { // In 32 bit mode, we can't handle files greater than 4 GiBs (don't use "NSUIntegerMax" here to avoid potential unsigned to signed conversion issues) + GWS_DNOT_REACHED(); + return nil; + } +#endif + NSUInteger fileSize = (NSUInteger)info.st_size; + + BOOL hasByteRange = GCDWebServerIsValidByteRange(range); + if (hasByteRange) { + if (range.location != NSUIntegerMax) { + range.location = MIN(range.location, fileSize); + range.length = MIN(range.length, fileSize - range.location); + } else { + range.length = MIN(range.length, fileSize); + range.location = fileSize - range.length; + } + if (range.length == 0) { + return nil; // TODO: Return 416 status code and "Content-Range: bytes */{file length}" header + } + } else { + range.location = 0; + range.length = fileSize; + } + + if ((self = [super init])) { + _path = [path copy]; + _offset = range.location; + _size = range.length; + if (hasByteRange) { + [self setStatusCode:kGCDWebServerHTTPStatusCode_PartialContent]; + [self setValue:[NSString stringWithFormat:@"bytes %lu-%lu/%lu", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), (unsigned long)fileSize] forAdditionalHeader:@"Content-Range"]; + GWS_LOG_DEBUG(@"Using content bytes range [%lu-%lu] for file \"%@\"", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), path); + } + + if (attachment) { + NSString* fileName = [path lastPathComponent]; + NSData* data = [[fileName stringByReplacingOccurrencesOfString:@"\"" withString:@""] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES]; + NSString* lossyFileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil; + if (lossyFileName) { + NSString* value = [NSString stringWithFormat:@"attachment; filename=\"%@\"; filename*=UTF-8''%@", lossyFileName, GCDWebServerEscapeURLString(fileName)]; + [self setValue:value forAdditionalHeader:@"Content-Disposition"]; + } else { + GWS_DNOT_REACHED(); + } + } + + self.contentType = GCDWebServerGetMimeTypeForExtension([_path pathExtension], overrides); + self.contentLength = _size; + self.lastModifiedDate = _NSDateFromTimeSpec(&info.st_mtimespec); + self.eTag = [NSString stringWithFormat:@"%llu/%li/%li", info.st_ino, info.st_mtimespec.tv_sec, info.st_mtimespec.tv_nsec]; + } + return self; +} + +- (BOOL)open:(NSError**)error { + _file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY); + if (_file <= 0) { + if (error) { + *error = GCDWebServerMakePosixError(errno); + } + return NO; + } + if (lseek(_file, _offset, SEEK_SET) != (off_t)_offset) { + if (error) { + *error = GCDWebServerMakePosixError(errno); + } + close(_file); + return NO; + } + return YES; +} + +- (NSData*)readData:(NSError**)error { + size_t length = MIN((NSUInteger)kFileReadBufferSize, _size); + NSMutableData* data = [[NSMutableData alloc] initWithLength:length]; + ssize_t result = read(_file, data.mutableBytes, length); + if (result < 0) { + if (error) { + *error = GCDWebServerMakePosixError(errno); + } + return nil; + } + if (result > 0) { + [data setLength:result]; + _size -= result; + } + return data; +} + +- (void)close { + close(_file); +} + +- (NSString*)description { + NSMutableString* description = [NSMutableString stringWithString:[super description]]; + [description appendFormat:@"\n\n{%@}", _path]; + return description; +} + +@end diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerStreamedResponse.h b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerStreamedResponse.h new file mode 100644 index 0000000..bb48e66 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerStreamedResponse.h @@ -0,0 +1,80 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GCDWebServerResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * The GCDWebServerStreamBlock is called to stream the data for the HTTP body. + * The block must return either a chunk of data, an empty NSData when done, or + * nil on error and set the "error" argument which is guaranteed to be non-NULL. + */ +typedef NSData* _Nullable (^GCDWebServerStreamBlock)(NSError** error); + +/** + * The GCDWebServerAsyncStreamBlock works like the GCDWebServerStreamBlock + * except the streamed data can be returned at a later time allowing for + * truly asynchronous generation of the data. + * + * The block must call "completionBlock" passing the new chunk of data when ready, + * an empty NSData when done, or nil on error and pass a NSError. + * + * The block cannot call "completionBlock" more than once per invocation. + */ +typedef void (^GCDWebServerAsyncStreamBlock)(GCDWebServerBodyReaderCompletionBlock completionBlock); + +/** + * The GCDWebServerStreamedResponse subclass of GCDWebServerResponse streams + * the body of the HTTP response using a GCD block. + */ +@interface GCDWebServerStreamedResponse : GCDWebServerResponse +@property(nonatomic, copy) NSString* contentType; // Redeclare as non-null + +/** + * Creates a response with streamed data and a given content type. + */ ++ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; + +/** + * Creates a response with async streamed data and a given content type. + */ ++ (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block; + +/** + * Initializes a response with streamed data and a given content type. + */ +- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; + +/** + * This method is the designated initializer for the class. + */ +- (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerStreamedResponse.m b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerStreamedResponse.m new file mode 100644 index 0000000..9387263 --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/GCDWebServer/Responses/GCDWebServerStreamedResponse.m @@ -0,0 +1,78 @@ +/* + Copyright (c) 2012-2015, Pierre-Olivier Latour + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !__has_feature(objc_arc) +#error GCDWebServer requires ARC +#endif + +#import "GCDWebServerPrivate.h" + +@implementation GCDWebServerStreamedResponse { + GCDWebServerAsyncStreamBlock _block; +} + +@dynamic contentType; + ++ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block { + return [[[self class] alloc] initWithContentType:type streamBlock:block]; +} + ++ (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block { + return [[[self class] alloc] initWithContentType:type asyncStreamBlock:block]; +} + +- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block { + return [self initWithContentType:type + asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) { + + NSError* error = nil; + NSData* data = block(&error); + completionBlock(data, error); + + }]; +} + +- (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block { + if ((self = [super init])) { + _block = [block copy]; + + self.contentType = type; + } + return self; +} + +- (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block { + _block(block); +} + +- (NSString*)description { + NSMutableString* description = [NSMutableString stringWithString:[super description]]; + [description appendString:@"\n\n"]; + return description; +} + +@end diff --git a/ios/Plugin/Pods/GCDWebServer/LICENSE b/ios/Plugin/Pods/GCDWebServer/LICENSE new file mode 100644 index 0000000..12335de --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2012-2014, Pierre-Olivier Latour +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ios/Plugin/Pods/GCDWebServer/README.md b/ios/Plugin/Pods/GCDWebServer/README.md new file mode 100644 index 0000000..b22211a --- /dev/null +++ b/ios/Plugin/Pods/GCDWebServer/README.md @@ -0,0 +1,470 @@ +Overview +======== + +[![Build Status](https://travis-ci.org/swisspol/GCDWebServer.svg?branch=master)](https://travis-ci.org/swisspol/GCDWebServer) +[![Version](http://cocoapod-badges.herokuapp.com/v/GCDWebServer/badge.png)](http://cocoadocs.org/docsets/GCDWebServer) +[![Platform](http://cocoapod-badges.herokuapp.com/p/GCDWebServer/badge.png)](https://github.com/swisspol/GCDWebServer) +[![License](http://img.shields.io/cocoapods/l/GCDWebServer.svg)](LICENSE) + +GCDWebServer is a modern and lightweight GCD based HTTP 1.1 server designed to be embedded in OS X & iOS apps. It was written from scratch with the following goals in mind: +* Elegant and easy to use architecture with only 4 core classes: server, connection, request and response (see "Understanding GCDWebServer's Architecture" below) +* Well designed API with fully documented headers for easy integration and customization +* Entirely built with an event-driven design using [Grand Central Dispatch](http://en.wikipedia.org/wiki/Grand_Central_Dispatch) for best performance and concurrency +* No dependencies on third-party source code +* Available under a friendly [New BSD License](LICENSE) + +Extra built-in features: +* Allow implementation of fully asynchronous handlers of incoming HTTP requests +* Minimize memory usage with disk streaming of large HTTP request or response bodies +* Parser for [web forms](http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4) submitted using "application/x-www-form-urlencoded" or "multipart/form-data" encodings (including file uploads) +* [JSON](http://www.json.org/) parsing and serialization for request and response HTTP bodies +* [Chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding) for request and response HTTP bodies +* [HTTP compression](https://en.wikipedia.org/wiki/HTTP_compression) with gzip for request and response HTTP bodies +* [HTTP range](https://en.wikipedia.org/wiki/Byte_serving) support for requests of local files +* [Basic](https://en.wikipedia.org/wiki/Basic_access_authentication) and [Digest Access](https://en.wikipedia.org/wiki/Digest_access_authentication) authentications for password protection +* Automatically handle transitions between foreground, background and suspended modes in iOS apps +* Full support for both IPv4 and IPv6 +* NAT port mapping (IPv4 only) + +Included extensions: +* [GCDWebUploader](GCDWebUploader/GCDWebUploader.h): subclass of ```GCDWebServer``` that implements an interface for uploading and downloading files using a web browser +* [GCDWebDAVServer](GCDWebDAVServer/GCDWebDAVServer.h): subclass of ```GCDWebServer``` that implements a class 1 [WebDAV](https://en.wikipedia.org/wiki/WebDAV) server (with partial class 2 support for OS X Finder) + +What's not supported (but not really required from an embedded HTTP server): +* Keep-alive connections +* HTTPS + +Requirements: +* OS X 10.7 or later (x86_64) +* iOS 8.0 or later (armv7, armv7s or arm64) +* ARC memory management only (if you need MRC support use GCDWebServer 3.1 and earlier) + +Getting Started +=============== + +Download or check out the [latest release](https://github.com/swisspol/GCDWebServer/releases) of GCDWebServer then add the entire "GCDWebServer" subfolder to your Xcode project. If you intend to use one of the extensions like GCDWebDAVServer or GCDWebUploader, add these subfolders as well. + +If you add the files directly then (1) link to `libz` (via Target > Build Phases > Link Binary With Libraries) and (2) add `$(SDKROOT)/usr/include/libxml2` to your header search paths (via Target > Build Settings > HEADER_SEARCH_PATHS). + +Alternatively, you can install GCDWebServer using [CocoaPods](http://cocoapods.org/) by simply adding this line to your Podfile: +``` +pod "GCDWebServer", "~> 3.0" +``` +If you want to use GCDWebUploader, use this line instead: +``` +pod "GCDWebServer/WebUploader", "~> 3.0" +``` +Or this line for GCDWebDAVServer: +``` +pod "GCDWebServer/WebDAV", "~> 3.0" +``` + +And finally run `$ pod install`. + +You can also use [Carthage](https://github.com/Carthage/Carthage) by adding this line to your Cartfile (3.2.5 is the first release with Carthage support): +``` +github "swisspol/GCDWebServer" ~> 3.2.5 +``` + +Then run `$ carthage update` and add the generated frameworks to your Xcode projects (see [Carthage instructions](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application)). + +Help & Support +============== + +For help with using GCDWebServer, it's best to ask your question on Stack Overflow with the [`gcdwebserver`](http://stackoverflow.com/questions/tagged/gcdwebserver) tag. For bug reports and enhancement requests you can use [issues](https://github.com/swisspol/GCDWebServer/issues) in this project. + +Be sure to read this entire README first though! + +Hello World +=========== + +These code snippets show how to implement a custom HTTP server that runs on port 8080 and returns a "Hello World" HTML page to any request. Since GCDWebServer uses GCD blocks to handle requests, no subclassing or delegates are needed, which results in very clean code. + +**IMPORTANT:** If not using CocoaPods, be sure to add the `libz` shared system library to the Xcode target for your app. + +**OS X version (command line tool):** +```objectivec +#import "GCDWebServer.h" +#import "GCDWebServerDataResponse.h" + +int main(int argc, const char* argv[]) { + @autoreleasepool { + + // Create server + GCDWebServer* webServer = [[GCDWebServer alloc] init]; + + // Add a handler to respond to GET requests on any URL + [webServer addDefaultHandlerForMethod:@"GET" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { + + return [GCDWebServerDataResponse responseWithHTML:@"

Hello World

"]; + + }]; + + // Use convenience method that runs server on port 8080 + // until SIGINT (Ctrl-C in Terminal) or SIGTERM is received + [webServer runWithPort:8080 bonjourName:nil]; + NSLog(@"Visit %@ in your web browser", webServer.serverURL); + + } + return 0; +} +``` + +**iOS version:** +```objectivec +#import "GCDWebServer.h" +#import "GCDWebServerDataResponse.h" + +@interface AppDelegate : NSObject { + GCDWebServer* _webServer; +} +@end + +@implementation AppDelegate + +- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + + // Create server + _webServer = [[GCDWebServer alloc] init]; + + // Add a handler to respond to GET requests on any URL + [_webServer addDefaultHandlerForMethod:@"GET" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { + + return [GCDWebServerDataResponse responseWithHTML:@"

Hello World

"]; + + }]; + + // Start server on port 8080 + [_webServer startWithPort:8080 bonjourName:nil]; + NSLog(@"Visit %@ in your web browser", _webServer.serverURL); + + return YES; +} + +@end +``` + +**OS X Swift version (command line tool):** + +***webServer.swift*** +```swift +import Foundation +import GCDWebServer + +func initWebServer() { + + let webServer = GCDWebServer() + + webServer.addDefaultHandlerForMethod("GET", requestClass: GCDWebServerRequest.self, processBlock: {request in + return GCDWebServerDataResponse(HTML:"

Hello World

") + + }) + + webServer.runWithPort(8080, bonjourName: "GCD Web Server") + + print("Visit \(webServer.serverURL) in your web browser") +} +``` + +***WebServer-Bridging-Header.h*** +```objectivec +#import +#import +``` + +Web Based Uploads in iOS Apps +============================= + +GCDWebUploader is a subclass of ```GCDWebServer``` that provides a ready-to-use HTML 5 file uploader & downloader. This lets users upload, download, delete files and create directories from a directory inside your iOS app's sandbox using a clean user interface in their web browser. + +Simply instantiate and run a ```GCDWebUploader``` instance then visit ```http://{YOUR-IOS-DEVICE-IP-ADDRESS}/``` from your web browser: + +```objectivec +#import "GCDWebUploader.h" + +@interface AppDelegate : NSObject { + GCDWebUploader* _webUploader; +} +@end + +@implementation AppDelegate + +- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; + _webUploader = [[GCDWebUploader alloc] initWithUploadDirectory:documentsPath]; + [_webUploader start]; + NSLog(@"Visit %@ in your web browser", _webUploader.serverURL); + return YES; +} + +@end +``` + +WebDAV Server in iOS Apps +========================= + +GCDWebDAVServer is a subclass of ```GCDWebServer``` that provides a class 1 compliant [WebDAV](https://en.wikipedia.org/wiki/WebDAV) server. This lets users upload, download, delete files and create directories from a directory inside your iOS app's sandbox using any WebDAV client like [Transmit](https://panic.com/transmit/) (Mac), [ForkLift](http://binarynights.com/forklift/) (Mac) or [CyberDuck](http://cyberduck.io/) (Mac / Windows). + +GCDWebDAVServer should also work with the [OS X Finder](http://support.apple.com/kb/PH13859) as it is partially class 2 compliant (but only when the client is the OS X WebDAV implementation). + +Simply instantiate and run a ```GCDWebDAVServer``` instance then connect to ```http://{YOUR-IOS-DEVICE-IP-ADDRESS}/``` using a WebDAV client: + +```objectivec +#import "GCDWebDAVServer.h" + +@interface AppDelegate : NSObject { + GCDWebDAVServer* _davServer; +} +@end + +@implementation AppDelegate + +- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; + _davServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:documentsPath]; + [_davServer start]; + NSLog(@"Visit %@ in your WebDAV client", _davServer.serverURL); + return YES; +} + +@end +``` + +Serving a Static Website +======================== + +GCDWebServer includes a built-in handler that can recursively serve a directory (it also lets you control how the ["Cache-Control"](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9) header should be set): + +**OS X version (command line tool):** +```objectivec +#import "GCDWebServer.h" + +int main(int argc, const char* argv[]) { + @autoreleasepool { + + GCDWebServer* webServer = [[GCDWebServer alloc] init]; + [webServer addGETHandlerForBasePath:@"/" directoryPath:NSHomeDirectory() indexFilename:nil cacheAge:3600 allowRangeRequests:YES]; + [webServer runWithPort:8080]; + + } + return 0; +} +``` + +Using GCDWebServer +================== + +You start by creating an instance of the ```GCDWebServer``` class. Note that you can have multiple web servers running in the same app as long as they listen on different ports. + +Then you add one or more "handlers" to the server: each handler gets a chance to handle an incoming web request and provide a response. Handlers are called in a LIFO queue, so the latest added handler overrides any previously added ones. + +Finally you start the server on a given port. + +Understanding GCDWebServer's Architecture +========================================= + +GCDWebServer's architecture consists of only 4 core classes: +* [GCDWebServer](GCDWebServer/Core/GCDWebServer.h) manages the socket that listens for new HTTP connections and the list of handlers used by the server. +* [GCDWebServerConnection](GCDWebServer/Core/GCDWebServerConnection.h) is instantiated by ```GCDWebServer``` to handle each new HTTP connection. Each instance stays alive until the connection is closed. You cannot use this class directly, but it is exposed so you can subclass it to override some hooks. +* [GCDWebServerRequest](GCDWebServer/Core/GCDWebServerRequest.h) is created by the ```GCDWebServerConnection``` instance after HTTP headers have been received. It wraps the request and handles the HTTP body if any. GCDWebServer comes with [several subclasses](GCDWebServer/Requests) of ```GCDWebServerRequest``` to handle common cases like storing the body in memory or stream it to a file on disk. +* [GCDWebServerResponse](GCDWebServer/Core/GCDWebServerResponse.h) is created by the request handler and wraps the response HTTP headers and optional body. GCDWebServer comes with [several subclasses](GCDWebServer/Responses) of ```GCDWebServerResponse``` to handle common cases like HTML text in memory or streaming a file from disk. + +Implementing Handlers +===================== + +GCDWebServer relies on "handlers" to process incoming web requests and generating responses. Handlers are implemented with GCD blocks which makes it very easy to provide your own. However, they are executed on arbitrary threads within GCD so __special attention must be paid to thread-safety and re-entrancy__. + +Handlers require 2 GCD blocks: +* The ```GCDWebServerMatchBlock``` is called on every handler added to the ```GCDWebServer``` instance whenever a web request has started (i.e. HTTP headers have been received). It is passed the basic info for the web request (HTTP method, URL, headers...) and must decide if it wants to handle it or not. If yes, it must return a new ```GCDWebServerRequest``` instance (see above) created with this info. Otherwise, it simply returns nil. +* The ```GCDWebServerProcessBlock``` or ```GCDWebServerAsyncProcessBlock``` is called after the web request has been fully received and is passed the ```GCDWebServerRequest``` instance created at the previous step. It must return synchronously (if using ```GCDWebServerProcessBlock```) or asynchronously (if using ```GCDWebServerAsyncProcessBlock```) a ```GCDWebServerResponse``` instance (see above) or nil on error, which will result in a 500 HTTP status code returned to the client. It's however recommended to return an instance of [GCDWebServerErrorResponse](GCDWebServer/Responses/GCDWebServerErrorResponse.h) on error so more useful information can be returned to the client. + +Note that most methods on ```GCDWebServer``` to add handlers only require the ```GCDWebServerProcessBlock``` or ```GCDWebServerAsyncProcessBlock``` as they already provide a built-in ```GCDWebServerMatchBlock``` e.g. to match a URL path with a Regex. + +Asynchronous HTTP Responses +=========================== + +New in GCDWebServer 3.0 is the ability to process HTTP requests asynchronously i.e. add handlers to the server which generate their ```GCDWebServerResponse``` asynchronously. This is achieved by adding handlers that use a ```GCDWebServerAsyncProcessBlock``` instead of a ```GCDWebServerProcessBlock```. Here's an example: + +**(Synchronous version)** The handler blocks while generating the HTTP response: +```objectivec +[webServer addDefaultHandlerForMethod:@"GET" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { + + GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithHTML:@"

Hello World

"]; + return response; + +}]; +``` + +**(Asynchronous version)** The handler returns immediately and calls back GCDWebServer later with the generated HTTP response: +```objectivec +[webServer addDefaultHandlerForMethod:@"GET" + requestClass:[GCDWebServerRequest class] + asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { + + // Do some async operation like network access or file I/O (simulated here using dispatch_after()) + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithHTML:@"

Hello World

"]; + completionBlock(response); + }); + +}]; +``` + +**(Advanced asynchronous version)** The handler returns immediately a streamed HTTP response which itself generates its contents asynchronously: +```objectivec +[webServer addDefaultHandlerForMethod:@"GET" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { + + NSMutableArray* contents = [NSMutableArray arrayWithObjects:@"

\n", @"Hello World!\n", @"

\n", nil]; // Fake data source we are reading from + GCDWebServerStreamedResponse* response = [GCDWebServerStreamedResponse responseWithContentType:@"text/html" asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) { + + // Simulate a delay reading from the fake data source + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSString* string = contents.firstObject; + if (string) { + [contents removeObjectAtIndex:0]; + completionBlock([string dataUsingEncoding:NSUTF8StringEncoding], nil); // Generate the 2nd part of the stream data + } else { + completionBlock([NSData data], nil); // Must pass an empty NSData to signal the end of the stream + } + }); + + }]; + return response; + +}]; +``` + +*Note that you can even combine both the asynchronous and advanced asynchronous versions to return asynchronously an asynchronous HTTP response!* + +GCDWebServer & Background Mode for iOS Apps +=========================================== + +When doing networking operations in iOS apps, you must handle carefully [what happens when iOS puts the app in the background](https://developer.apple.com/library/ios/technotes/tn2277/_index.html). Typically you must stop any network servers while the app is in the background and restart them when the app comes back to the foreground. This can become quite complex considering servers might have ongoing connections when they need to be stopped. + +Fortunately, GCDWebServer does all of this automatically for you: +- GCDWebServer begins a [background task](https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html) whenever the first HTTP connection is opened and ends it only when the last one is closed. This prevents iOS from suspending the app after it goes in the background, which would immediately kill HTTP connections to the client. + - While the app is in the background, as long as new HTTP connections keep being initiated, the background task will continue to exist and iOS will not suspend the app (unless under sudden and unexpected memory pressure). + - If the app is still in the background when the last HTTP connection is closed, GCDWebServer will suspend itself and stop accepting new connections as if you had called ```-stop``` (this behavior can be disabled with the ```GCDWebServerOption_AutomaticallySuspendInBackground``` option). +- If the app goes in the background while no HTTP connections are opened, GCDWebServer will immediately suspend itself and stop accepting new connections as if you had called ```-stop``` (this behavior can be disabled with the ```GCDWebServerOption_AutomaticallySuspendInBackground``` option). +- If the app comes back to the foreground and GCDWebServer had been suspended, it will automatically resume itself and start accepting again new HTTP connections as if you had called ```-start```. + +HTTP connections are often initiated in batches (or bursts), for instance when loading a web page with multiple resources. This makes it difficult to accurately detect when the *very last* HTTP connection has been closed: it's possible 2 consecutive HTTP connections part of the same batch would be separated by a small delay instead of overlapping. It would be bad for the client if GCDWebServer suspended itself right in between. The ```GCDWebServerOption_ConnectedStateCoalescingInterval``` option solves this problem elegantly by forcing GCDWebServer to wait some extra delay before performing any action after the last HTTP connection has been closed, just in case a new HTTP connection is initiated within this delay. + +Logging in GCDWebServer +======================= + +Both for debugging and informational purpose, GCDWebServer logs messages extensively whenever something happens. Furthermore, when building GCDWebServer in "Debug" mode versus "Release" mode, it logs even more information but also performs a number of internal consistency checks. To enable this behavior, define the preprocessor constant ```DEBUG=1``` when compiling GCDWebServer. In Xcode target settings, this can be done by adding ```DEBUG=1``` to the build setting ```GCC_PREPROCESSOR_DEFINITIONS``` when building in "Debug" configuration. Finally, you can also control the logging verbosity at run time by calling ```+[GCDWebServer setLogLevel:]```. + +By default, all messages logged by GCDWebServer are sent to its built-in logging facility, which simply outputs to ```stderr``` (assuming a terminal type device is connected). In order to better integrate with the rest of your app or because of the amount of information logged, you might want to use another logging facility. + +GCDWebServer has automatic support for [XLFacility](https://github.com/swisspol/XLFacility) (by the same author as GCDWebServer and also open-source): if it is in the same Xcode project, GCDWebServer should use it automatically instead of the built-in logging facility (see [GCDWebServerPrivate.h](GCDWebServer/Core/GCDWebServerPrivate.h) for the implementation details). + +It's also possible to use a custom logging facility - see [GCDWebServer.h](GCDWebServer/Core/GCDWebServer.h) for more information. + +Advanced Example 1: Implementing HTTP Redirects +=============================================== + +Here's an example handler that redirects "/" to "/index.html" using the convenience method on ```GCDWebServerResponse``` (it sets the HTTP status code and "Location" header automatically): + +```objectivec +[self addHandlerForMethod:@"GET" + path:@"/" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { + + return [GCDWebServerResponse responseWithRedirect:[NSURL URLWithString:@"index.html" relativeToURL:request.URL] + permanent:NO]; + +}]; +``` + +Advanced Example 2: Implementing Forms +====================================== + +To implement an HTTP form, you need a pair of handlers: +* The GET handler does not expect any body in the HTTP request and therefore uses the ```GCDWebServerRequest``` class. The handler generates a response containing a simple HTML form. +* The POST handler expects the form values to be in the body of the HTTP request and percent-encoded. Fortunately, GCDWebServer provides the request class ```GCDWebServerURLEncodedFormRequest``` which can automatically parse such bodies. The handler simply echoes back the value from the user submitted form. + +```objectivec +[webServer addHandlerForMethod:@"GET" + path:@"/" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { + + NSString* html = @" \ + \ +
\ + Value: \ + \ +
\ + \ + "; + return [GCDWebServerDataResponse responseWithHTML:html]; + +}]; + +[webServer addHandlerForMethod:@"POST" + path:@"/" + requestClass:[GCDWebServerURLEncodedFormRequest class] + processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { + + NSString* value = [[(GCDWebServerURLEncodedFormRequest*)request arguments] objectForKey:@"value"]; + NSString* html = [NSString stringWithFormat:@"

%@

", value]; + return [GCDWebServerDataResponse responseWithHTML:html]; + +}]; +``` + +Advanced Example 3: Serving a Dynamic Website +============================================= + +GCDWebServer provides an extension to the ```GCDWebServerDataResponse``` class that can return HTML content generated from a template and a set of variables (using the format ```%variable%```). It is a very basic template system and is really intended as a starting point to building more advanced template systems by subclassing ```GCDWebServerResponse```. + +Assuming you have a website directory in your app containing HTML template files along with the corresponding CSS, scripts and images, it's pretty easy to turn it into a dynamic website: + +```objectivec +// Get the path to the website directory +NSString* websitePath = [[NSBundle mainBundle] pathForResource:@"Website" ofType:nil]; + +// Add a default handler to serve static files (i.e. anything other than HTML files) +[self addGETHandlerForBasePath:@"/" directoryPath:websitePath indexFilename:nil cacheAge:3600 allowRangeRequests:YES]; + +// Add an override handler for all requests to "*.html" URLs to do the special HTML templatization +[self addHandlerForMethod:@"GET" + pathRegex:@"/.*\.html" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { + + NSDictionary* variables = [NSDictionary dictionaryWithObjectsAndKeys:@"value", @"variable", nil]; + return [GCDWebServerDataResponse responseWithHTMLTemplate:[websitePath stringByAppendingPathComponent:request.path] + variables:variables]; + +}]; + +// Add an override handler to redirect "/" URL to "/index.html" +[self addHandlerForMethod:@"GET" + path:@"/" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { + + return [GCDWebServerResponse responseWithRedirect:[NSURL URLWithString:@"index.html" relativeToURL:request.URL] + permanent:NO]; + +]; + +``` + +Final Example: File Downloads and Uploads From iOS App +====================================================== + +GCDWebServer was originally written for the [ComicFlow](http://itunes.apple.com/us/app/comicflow/id409290355?mt=8) comic reader app for iPad. It allow users to connect to their iPad with their web browser over WiFi and then upload, download and organize comic files inside the app. + +ComicFlow is [entirely open-source](https://github.com/swisspol/ComicFlow) and you can see how it uses GCDWebServer in the [WebServer.h](https://github.com/swisspol/ComicFlow/blob/master/Classes/WebServer.h) and [WebServer.m](https://github.com/swisspol/ComicFlow/blob/master/Classes/WebServer.m) files. diff --git a/ios/Plugin/Pods/Manifest.lock b/ios/Plugin/Pods/Manifest.lock new file mode 100644 index 0000000..487d066 --- /dev/null +++ b/ios/Plugin/Pods/Manifest.lock @@ -0,0 +1,20 @@ +PODS: + - Capacitor (0.0.8): + - CapacitorCordova (= 0.0.8) + - GCDWebServer (~> 3.0) + - CapacitorCordova (0.0.8) + - GCDWebServer (3.4.2): + - GCDWebServer/Core (= 3.4.2) + - GCDWebServer/Core (3.4.2) + +DEPENDENCIES: + - Capacitor + +SPEC CHECKSUMS: + Capacitor: 36a275e2fd85b69a2cb9633e8920ecf5c5aab88d + CapacitorCordova: cf26a9b075eb1f339118c920a975e4ff9e403193 + GCDWebServer: 8d67ee9f634b4bb91eb4b8aee440318a5fc6debd + +PODFILE CHECKSUM: 57e6d463d83fe9e2081ca17be52456b12c5e148c + +COCOAPODS: 1.4.0 diff --git a/ios/Plugin/Pods/Pods.xcodeproj/project.pbxproj b/ios/Plugin/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6fcec1f --- /dev/null +++ b/ios/Plugin/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,1463 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 043E6E9889071AAC35E5E72CCC26838E /* GCDWebServerHTTPStatusCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = AE78DEC8F31FAC6412386627F406AB18 /* GCDWebServerHTTPStatusCodes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 09EB1BF1776A906E5F882FC2EC132C92 /* GCDWebServerMultiPartFormRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = EF743E3EFC44782809FD45EBBB3C5D7B /* GCDWebServerMultiPartFormRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 09FE3A3409FC54A7DAE6C25E1879DDB7 /* Pods-Plugin-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D42DDF74C6DC607FBF6095BF3900279 /* Pods-Plugin-dummy.m */; }; + 0B5081DF930592B7F7C438C068ED482E /* CAPPluginCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9610DF35490929B49416B3952981F2DF /* CAPPluginCall.swift */; }; + 0C73050F432F69AE6F8CDD5733282C00 /* GCDWebServerRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = F9210BC97271D8E0DD8E23122AB217AF /* GCDWebServerRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0DC0A2F86F02A337CD17736BA5DB8319 /* GCDWebServerDataRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 238A87C710B2E382669EF89E7BD90B4A /* GCDWebServerDataRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0F288F27D3A53E980FEBA303603ADF9D /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = B45C7E23315CD2925B568D70EE4EEA6D /* Network.swift */; }; + 10F0E288C848B9198AF9F458E6C83954 /* Device.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98B34240BC52510E60D907544050EF81 /* Device.swift */; }; + 15E0EE338DA0037C05F6AAA61C83274D /* CDVAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E6F9161A8CCA1441E8469B038FE571F /* CDVAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1BC48A6CF72447C92CC2FEA464C26EE7 /* Diagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B5420B7A889DCE3FC1FABEC7D9645CF /* Diagnostics.swift */; }; + 1BF0170BA802C0ED323E504461BACCF0 /* DevMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3C2825426A14B887A7F7FDB3F5DCF76 /* DevMode.swift */; }; + 1DA33F4C5137B8CA2DD3AE44EC1F0631 /* CAPPluginMethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 19BCB1A1FD335D990F01037601CAC061 /* CAPPluginMethod.m */; }; + 1DDA06F0C75E5A21ACCBFF1F36BDC87F /* CAPPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = DA84790577AF703CD2A0751AB24A0D24 /* CAPPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 20403F4DE5E6B41098E276FDF2E91E8F /* CapacitorCordova-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 49DB4966E0F97B7B96357A69D700F9B9 /* CapacitorCordova-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 21F21A4F2FCB403EF0DE3FAB3F49AFF8 /* Clipboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFD4BB483303ED0597AF56A057FEDD5 /* Clipboard.swift */; }; + 275DBDE456DF7FDAFA2E252F5D136E9E /* Share.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09AE8D94243526194A70A6301C888049 /* Share.swift */; }; + 2B507CF8B7556050B28441172CA1DA75 /* GCDWebServer-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 99EE9AE4B273D740BFFB44DE08B20B29 /* GCDWebServer-dummy.m */; }; + 2C2DC089098BC4886AAF7850A1F7B77B /* CDVPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = F6448305DFF5F42C21664CC58AD8BDA2 /* CDVPlugin.m */; }; + 2C835A9222C22DD76B2500FCE816E469 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED7824BB651B215467415382C9FD7D74 /* Foundation.framework */; }; + 2F3DBAEA8252E28C952DA191C848FF4D /* JSExport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CA9896FC5B5AAAEA746F2E7ED6E9C40 /* JSExport.swift */; }; + 2F3EFB912B078E5BEBD5C6F956D9CAA2 /* JS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588BBEECAEE7F33F243247203FBB61CF /* JS.swift */; }; + 2F893645A50B732A9A36933230D56CA2 /* Pods-PluginTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 62492EEBA85C43145E6425D5017914C1 /* Pods-PluginTests-dummy.m */; }; + 39C0034EA5999011D40A4CD49BE3DE4B /* Pods-PluginTests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 327F5533726B8C7DAA70FB982C097983 /* Pods-PluginTests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3A069564DCC9253515B21EE9628C07E5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED7824BB651B215467415382C9FD7D74 /* Foundation.framework */; }; + 3BEB4AB2C590B15F4E8FD6A39244DCEA /* GCDWebServerFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 82931A51A703F596E4D0DEEEDAB480DE /* GCDWebServerFunctions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3D65F0D0A48C244A07E13BFF1839CFD6 /* CAPPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 36749BB2DE670E26DAB219959D328A39 /* CAPPlugin.m */; }; + 3FFD4DFDB81169A3CB7069C5D2E5FCF5 /* Keyboard.m in Sources */ = {isa = PBXBuildFile; fileRef = 12189FB9C617CDB5D9C31761440281F6 /* Keyboard.m */; }; + 4058FB98EED742291A20F1FE76551465 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AA878A1E75B490410375B1B5287808C /* Toast.swift */; }; + 44395BD8EB08535F9A73096B004906A3 /* GCDWebServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 04049DDC3199BED1378FF9A3792F1503 /* GCDWebServer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 44565DD3D720CA7A3345A545F177C67B /* CDVPluginResult.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1FDD8808DEE290ECBCA2118240BF2A /* CDVPluginResult.m */; }; + 46920A032FED74A322E909D4D01F07CF /* Capacitor.h in Headers */ = {isa = PBXBuildFile; fileRef = E5053102A6830FCD2DE509CE4AEE23F2 /* Capacitor.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 49841AD16EB4501EB30C9A41002FF939 /* GCDWebServerStreamedResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = EE85B786E92578FD3AB175811675C60E /* GCDWebServerStreamedResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 51303982C97E58544753A8EB2EE9C474 /* DefaultPlugins.m in Sources */ = {isa = PBXBuildFile; fileRef = 97D3D80F7055D878F5DB641702305946 /* DefaultPlugins.m */; }; + 529ED6BDD27B4EB3B874042C436F1973 /* Pods-Plugin-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = CAFDD613F87959EF5EA9D8441E437C53 /* Pods-Plugin-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 53225541FE1EFAC3B95DA67C0C6EAFBE /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 40919191DFA7768380316D30CDCE8676 /* MobileCoreServices.framework */; }; + 560734159E292543F01623E8EF30F6FC /* CAPPluginMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = 42D45A2531ADA3CECEAC0D240D5F91F0 /* CAPPluginMethod.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57F6D06F5F2D74F654190A8813F811F0 /* CAPPluginCall.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DE4BD3F889D0E13502A278849A7C797 /* CAPPluginCall.m */; }; + 5A06C00862B8DBE9C1C9200013BD88A8 /* CAPNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76049AD5317753BC253577A9F7CEAC64 /* CAPNotifications.swift */; }; + 5F0C483D1D41FDBBFD344CA5A2EDCAC7 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A98410DC8078BEA40A0B8E978A2AB2 /* App.swift */; }; + 60A3229749A6FA08ADB00363BB8EB9BF /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20A3E93DE3EEE832C44E9D4781175DA2 /* UIColor.swift */; }; + 616B13BF360B016934F0BF0AEB27AA76 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27A2B496D8EF37BCD5B0AA6CCF40534E /* WebKit.framework */; }; + 6338DE8191573141F7FFD7B864E6F3B8 /* GCDWebServerResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 2EE85AA0BE3B16D2640143DD9F189D32 /* GCDWebServerResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 68DA69B4293D2F46B37A19D4E3995308 /* GCDWebServerFileResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = BBB6E9ABF35760779A4FB444BF92A1C6 /* GCDWebServerFileResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 69691E6439A017FBA8A514F8B9D309D4 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E6FD2397BDC6E76D6C81299E796BEB1 /* CFNetwork.framework */; }; + 6BAB40AA93ADB79B575494EDBA8F0BE3 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = D5A38E9892C04A876C0164E71474A36C /* GCDWebServerFileResponse.m */; }; + 6BBD2C94FA0A8FCF85C644F3DCFC6B80 /* CDVInvokedUrlCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D9A050690A26CF7F3C24B4E759787DB5 /* CDVInvokedUrlCommand.m */; }; + 6C40FC1FBE78F4640335C07F540B96C9 /* CAPPluginCall.h in Headers */ = {isa = PBXBuildFile; fileRef = BBA548A0D2272B6F0B39A9DDF30DD3AE /* CAPPluginCall.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6CA2DD4AE85905D9E45A50955FC047B5 /* DocLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B18D6F6DB61087D1C8006A06F7211E /* DocLinks.swift */; }; + 6CAAE99014BD7E6F44D9860D44FCF9C8 /* CAPFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 609D6981E5053956F296DE8393B76191 /* CAPFile.swift */; }; + 6CB900534DF395C6601A1F8B8174DE7C /* PushNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211167FC38351DBDC84E0BFE1DF84E0A /* PushNotifications.swift */; }; + 6D17D5CAC54227E1DAE04F3BE7DD20B8 /* CDV.h in Headers */ = {isa = PBXBuildFile; fileRef = B0DB5BC19FEC623F1CBE34C0199ECEDF /* CDV.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6E21D9576D486B45EA11E4FF703780A8 /* CDVPluginResult.h in Headers */ = {isa = PBXBuildFile; fileRef = A79C4821C18E2A369BA6455D8603438E /* CDVPluginResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6E7F25F09B84381BBA36271087ED4BED /* Keyboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 35C6C5571A00AD3E7AADB4BD297CFB43 /* Keyboard.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6E854AC59BE60B56AC936036E06B44E0 /* CDVCommandDelegateImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = B4CB6A2A62642506A01015CE92E3130D /* CDVCommandDelegateImpl.m */; }; + 6F135094028F0A6E46F57BFDE9B42266 /* Camera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F69335AFC1D5B550C3431AB716524B /* Camera.swift */; }; + 6F4D1476A9ADCB6392F88A1B7859AD8E /* GCDWebServerStreamedResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C3BD6C463BE375744D2A29BA6DDB76D /* GCDWebServerStreamedResponse.m */; }; + 6FC75817C8971D747DC663F012C893E0 /* GCDWebServerFileRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA990B810AF10F64B436AD0B84D41796 /* GCDWebServerFileRequest.m */; }; + 708AD5084381D27FF7982E2E36128925 /* GCDWebServerDataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 108E16699B2DEF9AE07F4AEF5869B5F5 /* GCDWebServerDataRequest.m */; }; + 748D0E506BCB9164402093890274DED6 /* CapacitorCordova-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 6CDAE721290D5B06658B72653632BA75 /* CapacitorCordova-dummy.m */; }; + 76EAC3CBDD4A45CF9081E75E67F0593D /* GCDWebServerConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D378C9779249975BC5502F824BB89D6 /* GCDWebServerConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 77FF3D25D6B1FA613EF83AA41178B2C7 /* Modals.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4351079B6819DF4A70FA313A2F10851 /* Modals.swift */; }; + 78938767D0EA7F0B7F053570738E834C /* CDVInvokedUrlCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 04342480E00183EB8B7DF0F04924BD14 /* CDVInvokedUrlCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7AF590661E39B6828A3192E4C61B4B65 /* GCDWebServerFileRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = E3B22768445D10142B0DDA3F75AC0812 /* GCDWebServerFileRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7B3F12E3B58ED7028EE4B29BD83F8D3A /* GCDWebServer-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 275047C91568D2AF1897AF07F7D77565 /* GCDWebServer-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ED4D5453E24ED78A3472B1C3CD97872 /* GCDWebServerConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = B4952B06145AE4353FFC75980CAA95C6 /* GCDWebServerConnection.m */; }; + 7F999640850C5FDBDE12791E39D4D884 /* GCDWebServerErrorResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A98051B2698EA6C7EAB9472FF42403D /* GCDWebServerErrorResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 81BA50EF46280A6C468244D6CC6DC1C6 /* CapacitorCordova.h in Headers */ = {isa = PBXBuildFile; fileRef = 8070C34BD15568D6408180CA44ECB25D /* CapacitorCordova.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 87098BA586B1F09D72F014493C050550 /* GCDWebServerErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 7108676AEBBFAE3FBCE594D7CC6C117C /* GCDWebServerErrorResponse.m */; }; + 8D7549946638DE6F6BDAE007BB0DB955 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49E05D8A2AB0E460046F834FADD254ED /* Reachability.swift */; }; + 8D7B7C6B41A3F6B9A1E1BA5134FAB428 /* CDVPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CB2B21928DA7C37D4754B267DF0EA3D /* CDVPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8E19C32F6247A29B39590464B1572B20 /* Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7711B8DC62351443F0F43D18776B1B2 /* Accessibility.swift */; }; + 8E2AC03CCB7B0B15CCECD4D469A86FC9 /* GCDWebServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E3587CEB5BF4FAF5826471F7BC556A /* GCDWebServerResponse.m */; }; + 8EB4782A89D9AFD7D43D6B4F20736F57 /* GCDWebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = AF3336C40A175E543EEA23581AAFE2E3 /* GCDWebServer.m */; }; + 9082666CACBC16B3CC555BD6FF7CFC81 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E3DAB02A329213574DBE627B18A699 /* Filesystem.swift */; }; + 9426B26DA4DBFB33D748A0F243AB04DC /* CDVCommandDelegateImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = A8EEC7D6E5AD006BE212D95EF5C9BD38 /* CDVCommandDelegateImpl.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 960DDC45908F39283323BD09994A9434 /* GCDWebServerDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = A29E4BDC8AE640DD7FDF3490470F4C69 /* GCDWebServerDataResponse.m */; }; + 96DE6C36300A1B5B48040A32409FDBDB /* GCDWebServerMultiPartFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = AC0C427DC9FE1666F936B93F02AF95A4 /* GCDWebServerMultiPartFormRequest.m */; }; + 987EB65A3AF1FF11F07D2AB1253039AA /* CAPBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3799B8DA50F2C533A3260E4C4AA910BD /* CAPBridge.swift */; }; + 9C14F4B190D99DCAA09E4CC70468C34C /* Browser.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA4457CB1931C8A064CA7B4FCC374057 /* Browser.swift */; }; + 9CF024FDB0468F5C2DDF06EA5E3F3A2B /* CDVCommandDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = E47F2F9A3113BDA943C95B07AB7ED996 /* CDVCommandDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A50D54AA115920DAD039EAE4FEFDF868 /* CAPBridgedPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 55A450907DD961C2FF87FCC172220EC5 /* CAPBridgedPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A8430ADDE070336A355BC1C96068738A /* GCDWebServerURLEncodedFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = D8603AA4EB45CA3564F764F2A9A0FC33 /* GCDWebServerURLEncodedFormRequest.m */; }; + AF75C7CE199061BEE9387CA3AF930412 /* Photos.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D976BA285F7072CF018E099A9D7A08F /* Photos.swift */; }; + AFFCCFB13C9B3F76E8B677BC5B33E0F5 /* Cordova.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FFA934BBE4F908F8AAD2BB97C6AF57EE /* Cordova.framework */; }; + B1C30C80711C37521C41AA46B9AEFE81 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED7824BB651B215467415382C9FD7D74 /* Foundation.framework */; }; + B6C9E6799DAB16CF49183936BC21A4B0 /* CAPBridgeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE1069FC7E2B0FA3AE12A99F00918214 /* CAPBridgeViewController.swift */; }; + BF4690A69C1B90064044C2B2B29D25A7 /* GCDWebServerURLEncodedFormRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 887CDCB2893FCBC4C7FF0F38E6DCD1DE /* GCDWebServerURLEncodedFormRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C1E54E25CB130740EFA7C711B06EFB73 /* GCDWebServerFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = A59F65DEE3B97CADB06AFD91E58A6FFF /* GCDWebServerFunctions.m */; }; + C3BA481ED5EDC3F1B9D45B1A54875BB1 /* GCDWebServer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFD774900038CE0528FF325FD921FA45 /* GCDWebServer.framework */; }; + C51CC67DE4D57F92667322D20468E197 /* CAPBridgedPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = C74EB5177B24EC3318FED2296261B214 /* CAPBridgedPlugin.m */; }; + C72B67F6F8C7538BA359A751236F6EAB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED7824BB651B215467415382C9FD7D74 /* Foundation.framework */; }; + CCF6EAC8B78069231C94691365FA4F57 /* Haptics.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44FB442BE53E458D9F5841840A6FDE4 /* Haptics.swift */; }; + CD065EB5F8347DB027A249C99D7117C4 /* Geolocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 762E64B1BD1544A9DB3C4C1AE8CC42D9 /* Geolocation.swift */; }; + D3B2C6EAD0A87BCE0478E985A21125CD /* Capacitor-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E6867ACA691CA87AA35DCA16A20B3D2C /* Capacitor-dummy.m */; }; + D90ECA4AAA4A402BCD1C517F37D0915A /* GCDWebServerDataResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = E924A2F0AEFB4D1081CC0CBD1EBEB6C1 /* GCDWebServerDataResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DB1E729BF4D02B3E687CF18686DD2FD6 /* GCDWebServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = C9F2F7377CE8409EC28F618B4CEEB6AE /* GCDWebServerRequest.m */; }; + DB995C36E84724AF729FA6B62DA1F6C1 /* GCDWebServerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = F4B5F4EB0D2EFF225ACF6A25DCD3B715 /* GCDWebServerPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; + DBA11829B9248732CD4542D884C55CA4 /* Console.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81B6217A8910534212456385840DBA64 /* Console.swift */; }; + DBB67EB853BB43E3D122CA5465AB0DE6 /* LocalNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9970A99A443305EE5D03F344F836A1B1 /* LocalNotifications.swift */; }; + E07AA4482FBEF3CAFA1DC211593AAF26 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED7824BB651B215467415382C9FD7D74 /* Foundation.framework */; }; + E943E0AF33B124F40CFDC464F2249C11 /* SplashScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D7F8BEAB3EA5DBC78ECBD446E30493A /* SplashScreen.swift */; }; + EFC9A030D2F6AD974C166A6D2A6C4DCD /* DefaultPlugins.h in Headers */ = {isa = PBXBuildFile; fileRef = EC4B73DF383A3AD5A7988D295F98D834 /* DefaultPlugins.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FAA7816DF46AF9C96F037CC794177B7E /* Capacitor-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 15C99E7A03943AC5BEE1EAF3E0F1BB1A /* Capacitor-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FC0A97AF36AF3E76BE2698C867FA0C89 /* StatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 792ECE5D4A3610A91BA4A130981FF308 /* StatusBar.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 09A6C757E3A045EB962DED1B709C7895 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = F2C6209A9A06614635020AABB22C60A2; + remoteInfo = CapacitorCordova; + }; + 19E99F21FE7682C87EC8433510CB98F1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0D6BF964562CED21242461655F87C79A; + remoteInfo = GCDWebServer; + }; + 37107F2799C1A363FCE4F115333D56F0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = ECB48E98E0984198BBC4F65095530F94; + remoteInfo = Capacitor; + }; + 3D4C7DB4A3B64685AD2AA89117BC60F6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = F2C6209A9A06614635020AABB22C60A2; + remoteInfo = CapacitorCordova; + }; + 4E1AA4BC1611389AC010AB84C698F9F2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = F2C6209A9A06614635020AABB22C60A2; + remoteInfo = CapacitorCordova; + }; + 7A10114D4601FFC9F0865ECA596CAADD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = ECB48E98E0984198BBC4F65095530F94; + remoteInfo = Capacitor; + }; + CB1E80C9C57203C2F44F2A00B3DC250B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0D6BF964562CED21242461655F87C79A; + remoteInfo = GCDWebServer; + }; + EF86EF2C55C8C2D75C8377A435F99F5C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0D6BF964562CED21242461655F87C79A; + remoteInfo = GCDWebServer; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 04049DDC3199BED1378FF9A3792F1503 /* GCDWebServer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServer.h; path = GCDWebServer/Core/GCDWebServer.h; sourceTree = ""; }; + 04342480E00183EB8B7DF0F04924BD14 /* CDVInvokedUrlCommand.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CDVInvokedUrlCommand.h; path = ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.h; sourceTree = ""; }; + 04E3587CEB5BF4FAF5826471F7BC556A /* GCDWebServerResponse.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GCDWebServerResponse.m; path = GCDWebServer/Core/GCDWebServerResponse.m; sourceTree = ""; }; + 09AE8D94243526194A70A6301C888049 /* Share.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Share.swift; path = ios/Capacitor/Capacitor/Plugins/Share.swift; sourceTree = ""; }; + 0C3BD6C463BE375744D2A29BA6DDB76D /* GCDWebServerStreamedResponse.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GCDWebServerStreamedResponse.m; path = GCDWebServer/Responses/GCDWebServerStreamedResponse.m; sourceTree = ""; }; + 0D378C9779249975BC5502F824BB89D6 /* GCDWebServerConnection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServerConnection.h; path = GCDWebServer/Core/GCDWebServerConnection.h; sourceTree = ""; }; + 0E6F9161A8CCA1441E8469B038FE571F /* CDVAvailability.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CDVAvailability.h; path = ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVAvailability.h; sourceTree = ""; }; + 0F243C94C04E9548EAFE09CC7AF16922 /* Capacitor.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Capacitor.xcconfig; sourceTree = ""; }; + 108E16699B2DEF9AE07F4AEF5869B5F5 /* GCDWebServerDataRequest.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GCDWebServerDataRequest.m; path = GCDWebServer/Requests/GCDWebServerDataRequest.m; sourceTree = ""; }; + 12189FB9C617CDB5D9C31761440281F6 /* Keyboard.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = Keyboard.m; path = ios/Capacitor/Capacitor/Plugins/Keyboard.m; sourceTree = ""; }; + 15C99E7A03943AC5BEE1EAF3E0F1BB1A /* Capacitor-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Capacitor-umbrella.h"; sourceTree = ""; }; + 19BCB1A1FD335D990F01037601CAC061 /* CAPPluginMethod.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = CAPPluginMethod.m; path = ios/Capacitor/Capacitor/CAPPluginMethod.m; sourceTree = ""; }; + 1B5420B7A889DCE3FC1FABEC7D9645CF /* Diagnostics.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Diagnostics.swift; path = ios/Capacitor/Capacitor/Diagnostics.swift; sourceTree = ""; }; + 1BB25D85E54AB05ED1BD1803AC9D7348 /* Pods-PluginTests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = "Pods-PluginTests.modulemap"; sourceTree = ""; }; + 20A3E93DE3EEE832C44E9D4781175DA2 /* UIColor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UIColor.swift; path = ios/Capacitor/Capacitor/UIColor.swift; sourceTree = ""; }; + 211167FC38351DBDC84E0BFE1DF84E0A /* PushNotifications.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PushNotifications.swift; path = ios/Capacitor/Capacitor/Plugins/PushNotifications.swift; sourceTree = ""; }; + 23844A0F1A89B14D34F94A9E0FBCD2F0 /* CapacitorCordova.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = CapacitorCordova.xcconfig; sourceTree = ""; }; + 238A87C710B2E382669EF89E7BD90B4A /* GCDWebServerDataRequest.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServerDataRequest.h; path = GCDWebServer/Requests/GCDWebServerDataRequest.h; sourceTree = ""; }; + 275047C91568D2AF1897AF07F7D77565 /* GCDWebServer-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "GCDWebServer-umbrella.h"; sourceTree = ""; }; + 27A2B496D8EF37BCD5B0AA6CCF40534E /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; }; + 2E6FD2397BDC6E76D6C81299E796BEB1 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; + 2EE85AA0BE3B16D2640143DD9F189D32 /* GCDWebServerResponse.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServerResponse.h; path = GCDWebServer/Core/GCDWebServerResponse.h; sourceTree = ""; }; + 327F5533726B8C7DAA70FB982C097983 /* Pods-PluginTests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-PluginTests-umbrella.h"; sourceTree = ""; }; + 35C6C5571A00AD3E7AADB4BD297CFB43 /* Keyboard.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Keyboard.h; path = ios/Capacitor/Capacitor/Plugins/Keyboard.h; sourceTree = ""; }; + 36749BB2DE670E26DAB219959D328A39 /* CAPPlugin.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = CAPPlugin.m; path = ios/Capacitor/Capacitor/CAPPlugin.m; sourceTree = ""; }; + 3799B8DA50F2C533A3260E4C4AA910BD /* CAPBridge.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CAPBridge.swift; path = ios/Capacitor/Capacitor/CAPBridge.swift; sourceTree = ""; }; + 40919191DFA7768380316D30CDCE8676 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/MobileCoreServices.framework; sourceTree = DEVELOPER_DIR; }; + 42D45A2531ADA3CECEAC0D240D5F91F0 /* CAPPluginMethod.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CAPPluginMethod.h; path = ios/Capacitor/Capacitor/CAPPluginMethod.h; sourceTree = ""; }; + 438F88C054F05BB7899CB0A42E0E88D6 /* GCDWebServer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = GCDWebServer.framework; path = GCDWebServer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 44B274659DA6D8A45BA6133CBF6D5DE9 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 49DB4966E0F97B7B96357A69D700F9B9 /* CapacitorCordova-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "CapacitorCordova-umbrella.h"; sourceTree = ""; }; + 49E05D8A2AB0E460046F834FADD254ED /* Reachability.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Reachability.swift; path = ios/Capacitor/Capacitor/Plugins/Network/Reachability.swift; sourceTree = ""; }; + 4A184D30FC6C2616301DC1C8DB173FE8 /* CapacitorCordova.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = CapacitorCordova.modulemap; sourceTree = ""; }; + 4A98051B2698EA6C7EAB9472FF42403D /* GCDWebServerErrorResponse.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServerErrorResponse.h; path = GCDWebServer/Responses/GCDWebServerErrorResponse.h; sourceTree = ""; }; + 4AA878A1E75B490410375B1B5287808C /* Toast.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Toast.swift; path = ios/Capacitor/Capacitor/Plugins/Toast.swift; sourceTree = ""; }; + 4CB2B21928DA7C37D4754B267DF0EA3D /* CDVPlugin.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CDVPlugin.h; path = ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.h; sourceTree = ""; }; + 4CFD4BB483303ED0597AF56A057FEDD5 /* Clipboard.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Clipboard.swift; path = ios/Capacitor/Capacitor/Plugins/Clipboard.swift; sourceTree = ""; }; + 55A450907DD961C2FF87FCC172220EC5 /* CAPBridgedPlugin.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CAPBridgedPlugin.h; path = ios/Capacitor/Capacitor/CAPBridgedPlugin.h; sourceTree = ""; }; + 588BBEECAEE7F33F243247203FBB61CF /* JS.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = JS.swift; path = ios/Capacitor/Capacitor/JS.swift; sourceTree = ""; }; + 588F6EEFCB77499619C38377125A69D1 /* Pods-Plugin-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Plugin-acknowledgements.markdown"; sourceTree = ""; }; + 59129C0B04C613777AB8B590DD47AA2C /* Capacitor.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = Capacitor.modulemap; sourceTree = ""; }; + 5A7B65EEB25C8574C3B2C0A9AC7F5D36 /* GCDWebServer-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "GCDWebServer-prefix.pch"; sourceTree = ""; }; + 5D0442A24B87493D3D03D346D8B245B6 /* Pods_Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_Plugin.framework; path = "Pods-Plugin.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 607748F18E8B9B53DA5F1870DEC05003 /* Pods-PluginTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-PluginTests-acknowledgements.plist"; sourceTree = ""; }; + 609D6981E5053956F296DE8393B76191 /* CAPFile.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CAPFile.swift; path = ios/Capacitor/Capacitor/CAPFile.swift; sourceTree = ""; }; + 62492EEBA85C43145E6425D5017914C1 /* Pods-PluginTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-PluginTests-dummy.m"; sourceTree = ""; }; + 6CDAE721290D5B06658B72653632BA75 /* CapacitorCordova-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "CapacitorCordova-dummy.m"; sourceTree = ""; }; + 7108676AEBBFAE3FBCE594D7CC6C117C /* GCDWebServerErrorResponse.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GCDWebServerErrorResponse.m; path = GCDWebServer/Responses/GCDWebServerErrorResponse.m; sourceTree = ""; }; + 74B18D6F6DB61087D1C8006A06F7211E /* DocLinks.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DocLinks.swift; path = ios/Capacitor/Capacitor/DocLinks.swift; sourceTree = ""; }; + 74F69335AFC1D5B550C3431AB716524B /* Camera.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Camera.swift; path = ios/Capacitor/Capacitor/Plugins/Camera.swift; sourceTree = ""; }; + 76049AD5317753BC253577A9F7CEAC64 /* CAPNotifications.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CAPNotifications.swift; path = ios/Capacitor/Capacitor/CAPNotifications.swift; sourceTree = ""; }; + 762E64B1BD1544A9DB3C4C1AE8CC42D9 /* Geolocation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Geolocation.swift; path = ios/Capacitor/Capacitor/Plugins/Geolocation.swift; sourceTree = ""; }; + 792ECE5D4A3610A91BA4A130981FF308 /* StatusBar.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StatusBar.swift; path = ios/Capacitor/Capacitor/Plugins/StatusBar.swift; sourceTree = ""; }; + 7D42DDF74C6DC607FBF6095BF3900279 /* Pods-Plugin-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Plugin-dummy.m"; sourceTree = ""; }; + 7D976BA285F7072CF018E099A9D7A08F /* Photos.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Photos.swift; path = ios/Capacitor/Capacitor/Plugins/Photos/Photos.swift; sourceTree = ""; }; + 7DE4BD3F889D0E13502A278849A7C797 /* CAPPluginCall.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = CAPPluginCall.m; path = ios/Capacitor/Capacitor/CAPPluginCall.m; sourceTree = ""; }; + 8070C34BD15568D6408180CA44ECB25D /* CapacitorCordova.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CapacitorCordova.h; path = ios/CapacitorCordova/CapacitorCordova/CapacitorCordova.h; sourceTree = ""; }; + 81B6217A8910534212456385840DBA64 /* Console.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Console.swift; path = ios/Capacitor/Capacitor/Plugins/Console.swift; sourceTree = ""; }; + 82931A51A703F596E4D0DEEEDAB480DE /* GCDWebServerFunctions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServerFunctions.h; path = GCDWebServer/Core/GCDWebServerFunctions.h; sourceTree = ""; }; + 84E3DAB02A329213574DBE627B18A699 /* Filesystem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Filesystem.swift; path = ios/Capacitor/Capacitor/Plugins/Filesystem.swift; sourceTree = ""; }; + 8690DE655FAD17B6DBE01F3721865DBE /* GCDWebServer.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = GCDWebServer.modulemap; sourceTree = ""; }; + 871AF3084993B6A24A46CEA3C4ACE39E /* Pods-Plugin.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Plugin.release.xcconfig"; sourceTree = ""; }; + 887CDCB2893FCBC4C7FF0F38E6DCD1DE /* GCDWebServerURLEncodedFormRequest.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServerURLEncodedFormRequest.h; path = GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.h; sourceTree = ""; }; + 8D75C3C44585E6E7820F02F56D5FE300 /* Pods-Plugin-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Plugin-resources.sh"; sourceTree = ""; }; + 8D7F8BEAB3EA5DBC78ECBD446E30493A /* SplashScreen.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SplashScreen.swift; path = ios/Capacitor/Capacitor/Plugins/SplashScreen.swift; sourceTree = ""; }; + 8EC3E52DA7F2E6794DE519AFAA0C6CED /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 918EDBE7786E4237E3CBDDD3A07CBB5C /* CapacitorCordova-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "CapacitorCordova-prefix.pch"; sourceTree = ""; }; + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 9610DF35490929B49416B3952981F2DF /* CAPPluginCall.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CAPPluginCall.swift; path = ios/Capacitor/Capacitor/CAPPluginCall.swift; sourceTree = ""; }; + 97D3D80F7055D878F5DB641702305946 /* DefaultPlugins.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = DefaultPlugins.m; path = ios/Capacitor/Capacitor/Plugins/DefaultPlugins.m; sourceTree = ""; }; + 98B34240BC52510E60D907544050EF81 /* Device.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Device.swift; path = ios/Capacitor/Capacitor/Plugins/Device.swift; sourceTree = ""; }; + 9970A99A443305EE5D03F344F836A1B1 /* LocalNotifications.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LocalNotifications.swift; path = ios/Capacitor/Capacitor/Plugins/LocalNotifications.swift; sourceTree = ""; }; + 99EE9AE4B273D740BFFB44DE08B20B29 /* GCDWebServer-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "GCDWebServer-dummy.m"; sourceTree = ""; }; + 9CA9896FC5B5AAAEA746F2E7ED6E9C40 /* JSExport.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = JSExport.swift; path = ios/Capacitor/Capacitor/JSExport.swift; sourceTree = ""; }; + A29E4BDC8AE640DD7FDF3490470F4C69 /* GCDWebServerDataResponse.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GCDWebServerDataResponse.m; path = GCDWebServer/Responses/GCDWebServerDataResponse.m; sourceTree = ""; }; + A59F65DEE3B97CADB06AFD91E58A6FFF /* GCDWebServerFunctions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GCDWebServerFunctions.m; path = GCDWebServer/Core/GCDWebServerFunctions.m; sourceTree = ""; }; + A79C4821C18E2A369BA6455D8603438E /* CDVPluginResult.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CDVPluginResult.h; path = ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.h; sourceTree = ""; }; + A8EEC7D6E5AD006BE212D95EF5C9BD38 /* CDVCommandDelegateImpl.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CDVCommandDelegateImpl.h; path = ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.h; sourceTree = ""; }; + AA4457CB1931C8A064CA7B4FCC374057 /* Browser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Browser.swift; path = ios/Capacitor/Capacitor/Plugins/Browser.swift; sourceTree = ""; }; + AA546E784C186D64018A741EE9E9A36A /* Pods-Plugin.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = "Pods-Plugin.modulemap"; sourceTree = ""; }; + AC0C427DC9FE1666F936B93F02AF95A4 /* GCDWebServerMultiPartFormRequest.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GCDWebServerMultiPartFormRequest.m; path = GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.m; sourceTree = ""; }; + AD2AF8B23415268D1796D3C648978A10 /* Pods-PluginTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-PluginTests-acknowledgements.markdown"; sourceTree = ""; }; + AE78DEC8F31FAC6412386627F406AB18 /* GCDWebServerHTTPStatusCodes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServerHTTPStatusCodes.h; path = GCDWebServer/Core/GCDWebServerHTTPStatusCodes.h; sourceTree = ""; }; + AF3336C40A175E543EEA23581AAFE2E3 /* GCDWebServer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GCDWebServer.m; path = GCDWebServer/Core/GCDWebServer.m; sourceTree = ""; }; + B0DB5BC19FEC623F1CBE34C0199ECEDF /* CDV.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CDV.h; path = ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDV.h; sourceTree = ""; }; + B45C7E23315CD2925B568D70EE4EEA6D /* Network.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Network.swift; path = ios/Capacitor/Capacitor/Plugins/Network/Network.swift; sourceTree = ""; }; + B4952B06145AE4353FFC75980CAA95C6 /* GCDWebServerConnection.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GCDWebServerConnection.m; path = GCDWebServer/Core/GCDWebServerConnection.m; sourceTree = ""; }; + B4CB6A2A62642506A01015CE92E3130D /* CDVCommandDelegateImpl.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = CDVCommandDelegateImpl.m; path = ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegateImpl.m; sourceTree = ""; }; + B829A9F4584637BB0C6A4D2472881165 /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Capacitor.framework; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B976A28B6CF4200AC9E2ED1197803AC6 /* Pods-Plugin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Plugin.debug.xcconfig"; sourceTree = ""; }; + B9D773079AF0ACA53F33A135CEF3FDA9 /* Pods-PluginTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-PluginTests.release.xcconfig"; sourceTree = ""; }; + BBA548A0D2272B6F0B39A9DDF30DD3AE /* CAPPluginCall.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CAPPluginCall.h; path = ios/Capacitor/Capacitor/CAPPluginCall.h; sourceTree = ""; }; + BBB6E9ABF35760779A4FB444BF92A1C6 /* GCDWebServerFileResponse.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServerFileResponse.h; path = GCDWebServer/Responses/GCDWebServerFileResponse.h; sourceTree = ""; }; + BD4AE95D50875216D1A98BB5546A39C5 /* Cordova.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Cordova.framework; path = CapacitorCordova.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BE1069FC7E2B0FA3AE12A99F00918214 /* CAPBridgeViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CAPBridgeViewController.swift; path = ios/Capacitor/Capacitor/CAPBridgeViewController.swift; sourceTree = ""; }; + C00EFC7A7479E8544813C1C8FD7C78C9 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C4351079B6819DF4A70FA313A2F10851 /* Modals.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Modals.swift; path = ios/Capacitor/Capacitor/Plugins/Modals.swift; sourceTree = ""; }; + C472A9B605FF956581FD5542255434D6 /* GCDWebServer.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = GCDWebServer.xcconfig; sourceTree = ""; }; + C74EB5177B24EC3318FED2296261B214 /* CAPBridgedPlugin.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = CAPBridgedPlugin.m; path = ios/Capacitor/Capacitor/CAPBridgedPlugin.m; sourceTree = ""; }; + C9F2F7377CE8409EC28F618B4CEEB6AE /* GCDWebServerRequest.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GCDWebServerRequest.m; path = GCDWebServer/Core/GCDWebServerRequest.m; sourceTree = ""; }; + CA990B810AF10F64B436AD0B84D41796 /* GCDWebServerFileRequest.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GCDWebServerFileRequest.m; path = GCDWebServer/Requests/GCDWebServerFileRequest.m; sourceTree = ""; }; + CAFDD613F87959EF5EA9D8441E437C53 /* Pods-Plugin-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Plugin-umbrella.h"; sourceTree = ""; }; + CBBDAC4B05EFB43667A5A8195065B281 /* Pods-PluginTests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-PluginTests-frameworks.sh"; sourceTree = ""; }; + CBDACF7B82B770C062018DCF356165E2 /* Pods-Plugin-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Plugin-acknowledgements.plist"; sourceTree = ""; }; + CFA5DB5FF1E20C89D235BC74729C4C55 /* Capacitor-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Capacitor-prefix.pch"; sourceTree = ""; }; + D44FB442BE53E458D9F5841840A6FDE4 /* Haptics.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Haptics.swift; path = ios/Capacitor/Capacitor/Plugins/Haptics.swift; sourceTree = ""; }; + D5A38E9892C04A876C0164E71474A36C /* GCDWebServerFileResponse.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GCDWebServerFileResponse.m; path = GCDWebServer/Responses/GCDWebServerFileResponse.m; sourceTree = ""; }; + D720E9798FD143AEE1AFE15D4CF22011 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D8603AA4EB45CA3564F764F2A9A0FC33 /* GCDWebServerURLEncodedFormRequest.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GCDWebServerURLEncodedFormRequest.m; path = GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.m; sourceTree = ""; }; + D9A050690A26CF7F3C24B4E759787DB5 /* CDVInvokedUrlCommand.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = CDVInvokedUrlCommand.m; path = ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVInvokedUrlCommand.m; sourceTree = ""; }; + D9BBAF6C6ED8BF5E4B9F8B042C22591D /* Pods_PluginTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_PluginTests.framework; path = "Pods-PluginTests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + DA84790577AF703CD2A0751AB24A0D24 /* CAPPlugin.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CAPPlugin.h; path = ios/Capacitor/Capacitor/CAPPlugin.h; sourceTree = ""; }; + DFD774900038CE0528FF325FD921FA45 /* GCDWebServer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GCDWebServer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E0A569010CBAEB4EF5B9FE69D8F39299 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E2A98410DC8078BEA40A0B8E978A2AB2 /* App.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = App.swift; path = ios/Capacitor/Capacitor/Plugins/App.swift; sourceTree = ""; }; + E3B22768445D10142B0DDA3F75AC0812 /* GCDWebServerFileRequest.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServerFileRequest.h; path = GCDWebServer/Requests/GCDWebServerFileRequest.h; sourceTree = ""; }; + E3C2825426A14B887A7F7FDB3F5DCF76 /* DevMode.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DevMode.swift; path = ios/Capacitor/Capacitor/DevMode.swift; sourceTree = ""; }; + E47F2F9A3113BDA943C95B07AB7ED996 /* CDVCommandDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = CDVCommandDelegate.h; path = ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVCommandDelegate.h; sourceTree = ""; }; + E5053102A6830FCD2DE509CE4AEE23F2 /* Capacitor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Capacitor.h; path = ios/Capacitor/Capacitor/Capacitor.h; sourceTree = ""; }; + E6867ACA691CA87AA35DCA16A20B3D2C /* Capacitor-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Capacitor-dummy.m"; sourceTree = ""; }; + E7711B8DC62351443F0F43D18776B1B2 /* Accessibility.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Accessibility.swift; path = ios/Capacitor/Capacitor/Plugins/Accessibility.swift; sourceTree = ""; }; + E924A2F0AEFB4D1081CC0CBD1EBEB6C1 /* GCDWebServerDataResponse.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServerDataResponse.h; path = GCDWebServer/Responses/GCDWebServerDataResponse.h; sourceTree = ""; }; + EC4B73DF383A3AD5A7988D295F98D834 /* DefaultPlugins.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DefaultPlugins.h; path = ios/Capacitor/Capacitor/Plugins/DefaultPlugins.h; sourceTree = ""; }; + ED7824BB651B215467415382C9FD7D74 /* 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; }; + EE85B786E92578FD3AB175811675C60E /* GCDWebServerStreamedResponse.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServerStreamedResponse.h; path = GCDWebServer/Responses/GCDWebServerStreamedResponse.h; sourceTree = ""; }; + EF743E3EFC44782809FD45EBBB3C5D7B /* GCDWebServerMultiPartFormRequest.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServerMultiPartFormRequest.h; path = GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.h; sourceTree = ""; }; + F4B5F4EB0D2EFF225ACF6A25DCD3B715 /* GCDWebServerPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServerPrivate.h; path = GCDWebServer/Core/GCDWebServerPrivate.h; sourceTree = ""; }; + F6448305DFF5F42C21664CC58AD8BDA2 /* CDVPlugin.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = CDVPlugin.m; path = ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPlugin.m; sourceTree = ""; }; + F688B7298929A01E6891D1A8B343651E /* Pods-PluginTests-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-PluginTests-resources.sh"; sourceTree = ""; }; + F9210BC97271D8E0DD8E23122AB217AF /* GCDWebServerRequest.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GCDWebServerRequest.h; path = GCDWebServer/Core/GCDWebServerRequest.h; sourceTree = ""; }; + FAADC53959C773CBD73D65D23C6797FB /* Pods-PluginTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-PluginTests.debug.xcconfig"; sourceTree = ""; }; + FC1FDD8808DEE290ECBCA2118240BF2A /* CDVPluginResult.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = CDVPluginResult.m; path = ios/CapacitorCordova/CapacitorCordova/Classes/Public/CDVPluginResult.m; sourceTree = ""; }; + FFA934BBE4F908F8AAD2BB97C6AF57EE /* Cordova.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cordova.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 21BDA92483318B212FC10E7EC1883359 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B1C30C80711C37521C41AA46B9AEFE81 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 636258FD9AA272C1BCF942157BED9F72 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 69691E6439A017FBA8A514F8B9D309D4 /* CFNetwork.framework in Frameworks */, + 3A069564DCC9253515B21EE9628C07E5 /* Foundation.framework in Frameworks */, + 53225541FE1EFAC3B95DA67C0C6EAFBE /* MobileCoreServices.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E0E4E15EC3295DA3D09CAFC72BA4263B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E07AA4482FBEF3CAFA1DC211593AAF26 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EA987A8F80A00AFCA0DEC80FAE85FDF3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2C835A9222C22DD76B2500FCE816E469 /* Foundation.framework in Frameworks */, + 616B13BF360B016934F0BF0AEB27AA76 /* WebKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FD91981E66CFF57D6FFD3C86B424B5C7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AFFCCFB13C9B3F76E8B677BC5B33E0F5 /* Cordova.framework in Frameworks */, + C72B67F6F8C7538BA359A751236F6EAB /* Foundation.framework in Frameworks */, + C3BA481ED5EDC3F1B9D45B1A54875BB1 /* GCDWebServer.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0D752B23BB5145427D34591093E254DB /* Support Files */ = { + isa = PBXGroup; + children = ( + 8690DE655FAD17B6DBE01F3721865DBE /* GCDWebServer.modulemap */, + C472A9B605FF956581FD5542255434D6 /* GCDWebServer.xcconfig */, + 99EE9AE4B273D740BFFB44DE08B20B29 /* GCDWebServer-dummy.m */, + 5A7B65EEB25C8574C3B2C0A9AC7F5D36 /* GCDWebServer-prefix.pch */, + 275047C91568D2AF1897AF07F7D77565 /* GCDWebServer-umbrella.h */, + E0A569010CBAEB4EF5B9FE69D8F39299 /* Info.plist */, + ); + name = "Support Files"; + path = "../Target Support Files/GCDWebServer"; + sourceTree = ""; + }; + 0E99FC2E9AE87C09F892D47EA5138A0D /* Capacitor */ = { + isa = PBXGroup; + children = ( + E7711B8DC62351443F0F43D18776B1B2 /* Accessibility.swift */, + E2A98410DC8078BEA40A0B8E978A2AB2 /* App.swift */, + AA4457CB1931C8A064CA7B4FCC374057 /* Browser.swift */, + 74F69335AFC1D5B550C3431AB716524B /* Camera.swift */, + E5053102A6830FCD2DE509CE4AEE23F2 /* Capacitor.h */, + 3799B8DA50F2C533A3260E4C4AA910BD /* CAPBridge.swift */, + 55A450907DD961C2FF87FCC172220EC5 /* CAPBridgedPlugin.h */, + C74EB5177B24EC3318FED2296261B214 /* CAPBridgedPlugin.m */, + BE1069FC7E2B0FA3AE12A99F00918214 /* CAPBridgeViewController.swift */, + 609D6981E5053956F296DE8393B76191 /* CAPFile.swift */, + 76049AD5317753BC253577A9F7CEAC64 /* CAPNotifications.swift */, + DA84790577AF703CD2A0751AB24A0D24 /* CAPPlugin.h */, + 36749BB2DE670E26DAB219959D328A39 /* CAPPlugin.m */, + BBA548A0D2272B6F0B39A9DDF30DD3AE /* CAPPluginCall.h */, + 7DE4BD3F889D0E13502A278849A7C797 /* CAPPluginCall.m */, + 9610DF35490929B49416B3952981F2DF /* CAPPluginCall.swift */, + 42D45A2531ADA3CECEAC0D240D5F91F0 /* CAPPluginMethod.h */, + 19BCB1A1FD335D990F01037601CAC061 /* CAPPluginMethod.m */, + 4CFD4BB483303ED0597AF56A057FEDD5 /* Clipboard.swift */, + 81B6217A8910534212456385840DBA64 /* Console.swift */, + EC4B73DF383A3AD5A7988D295F98D834 /* DefaultPlugins.h */, + 97D3D80F7055D878F5DB641702305946 /* DefaultPlugins.m */, + 98B34240BC52510E60D907544050EF81 /* Device.swift */, + E3C2825426A14B887A7F7FDB3F5DCF76 /* DevMode.swift */, + 1B5420B7A889DCE3FC1FABEC7D9645CF /* Diagnostics.swift */, + 74B18D6F6DB61087D1C8006A06F7211E /* DocLinks.swift */, + 84E3DAB02A329213574DBE627B18A699 /* Filesystem.swift */, + 762E64B1BD1544A9DB3C4C1AE8CC42D9 /* Geolocation.swift */, + D44FB442BE53E458D9F5841840A6FDE4 /* Haptics.swift */, + 588BBEECAEE7F33F243247203FBB61CF /* JS.swift */, + 9CA9896FC5B5AAAEA746F2E7ED6E9C40 /* JSExport.swift */, + 35C6C5571A00AD3E7AADB4BD297CFB43 /* Keyboard.h */, + 12189FB9C617CDB5D9C31761440281F6 /* Keyboard.m */, + 9970A99A443305EE5D03F344F836A1B1 /* LocalNotifications.swift */, + C4351079B6819DF4A70FA313A2F10851 /* Modals.swift */, + B45C7E23315CD2925B568D70EE4EEA6D /* Network.swift */, + 7D976BA285F7072CF018E099A9D7A08F /* Photos.swift */, + 211167FC38351DBDC84E0BFE1DF84E0A /* PushNotifications.swift */, + 49E05D8A2AB0E460046F834FADD254ED /* Reachability.swift */, + 09AE8D94243526194A70A6301C888049 /* Share.swift */, + 8D7F8BEAB3EA5DBC78ECBD446E30493A /* SplashScreen.swift */, + 792ECE5D4A3610A91BA4A130981FF308 /* StatusBar.swift */, + 4AA878A1E75B490410375B1B5287808C /* Toast.swift */, + 20A3E93DE3EEE832C44E9D4781175DA2 /* UIColor.swift */, + 4A555A2C9AA2246F92D0BDEAB46FFEBA /* Support Files */, + ); + name = Capacitor; + path = Capacitor; + sourceTree = ""; + }; + 0E9A1C47D71B1770DEE03CCA98E16551 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + 2D85CA5F625594EAAEE5700B5FC8D160 /* Pods-Plugin */, + F4368CB138A433FA424575144A309188 /* Pods-PluginTests */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + 2D85CA5F625594EAAEE5700B5FC8D160 /* Pods-Plugin */ = { + isa = PBXGroup; + children = ( + D720E9798FD143AEE1AFE15D4CF22011 /* Info.plist */, + AA546E784C186D64018A741EE9E9A36A /* Pods-Plugin.modulemap */, + 588F6EEFCB77499619C38377125A69D1 /* Pods-Plugin-acknowledgements.markdown */, + CBDACF7B82B770C062018DCF356165E2 /* Pods-Plugin-acknowledgements.plist */, + 7D42DDF74C6DC607FBF6095BF3900279 /* Pods-Plugin-dummy.m */, + 8D75C3C44585E6E7820F02F56D5FE300 /* Pods-Plugin-resources.sh */, + CAFDD613F87959EF5EA9D8441E437C53 /* Pods-Plugin-umbrella.h */, + B976A28B6CF4200AC9E2ED1197803AC6 /* Pods-Plugin.debug.xcconfig */, + 871AF3084993B6A24A46CEA3C4ACE39E /* Pods-Plugin.release.xcconfig */, + ); + name = "Pods-Plugin"; + path = "Target Support Files/Pods-Plugin"; + sourceTree = ""; + }; + 4A555A2C9AA2246F92D0BDEAB46FFEBA /* Support Files */ = { + isa = PBXGroup; + children = ( + 59129C0B04C613777AB8B590DD47AA2C /* Capacitor.modulemap */, + 0F243C94C04E9548EAFE09CC7AF16922 /* Capacitor.xcconfig */, + E6867ACA691CA87AA35DCA16A20B3D2C /* Capacitor-dummy.m */, + CFA5DB5FF1E20C89D235BC74729C4C55 /* Capacitor-prefix.pch */, + 15C99E7A03943AC5BEE1EAF3E0F1BB1A /* Capacitor-umbrella.h */, + 8EC3E52DA7F2E6794DE519AFAA0C6CED /* Info.plist */, + ); + name = "Support Files"; + path = "../Target Support Files/Capacitor"; + sourceTree = ""; + }; + 5BCCB4FC3285E308E950D89D243E4C97 /* Frameworks */ = { + isa = PBXGroup; + children = ( + FFA934BBE4F908F8AAD2BB97C6AF57EE /* Cordova.framework */, + DFD774900038CE0528FF325FD921FA45 /* GCDWebServer.framework */, + DEF95E85DA0C96A28DEC7A0574C21995 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + 75D53CF9FE86FE90822CB5932A9976F2 /* GCDWebServer */ = { + isa = PBXGroup; + children = ( + D978EF995AB434D7172648A0960C0E7C /* Core */, + 0D752B23BB5145427D34591093E254DB /* Support Files */, + ); + name = GCDWebServer; + path = GCDWebServer; + sourceTree = ""; + }; + 7DB346D0F39D3F0E887471402A8071AB = { + isa = PBXGroup; + children = ( + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, + 5BCCB4FC3285E308E950D89D243E4C97 /* Frameworks */, + 9AC68B6CFD5A4D948158C00FB44AA8EC /* Pods */, + 828DC58EE2DE2C4AB33FE717A10939A2 /* Products */, + 0E9A1C47D71B1770DEE03CCA98E16551 /* Targets Support Files */, + ); + sourceTree = ""; + }; + 828DC58EE2DE2C4AB33FE717A10939A2 /* Products */ = { + isa = PBXGroup; + children = ( + B829A9F4584637BB0C6A4D2472881165 /* Capacitor.framework */, + BD4AE95D50875216D1A98BB5546A39C5 /* Cordova.framework */, + 438F88C054F05BB7899CB0A42E0E88D6 /* GCDWebServer.framework */, + 5D0442A24B87493D3D03D346D8B245B6 /* Pods_Plugin.framework */, + D9BBAF6C6ED8BF5E4B9F8B042C22591D /* Pods_PluginTests.framework */, + ); + name = Products; + sourceTree = ""; + }; + 9AC68B6CFD5A4D948158C00FB44AA8EC /* Pods */ = { + isa = PBXGroup; + children = ( + 0E99FC2E9AE87C09F892D47EA5138A0D /* Capacitor */, + BE33BFDE43CD4A64C6DF8B300BF979D7 /* CapacitorCordova */, + 75D53CF9FE86FE90822CB5932A9976F2 /* GCDWebServer */, + ); + name = Pods; + sourceTree = ""; + }; + BE33BFDE43CD4A64C6DF8B300BF979D7 /* CapacitorCordova */ = { + isa = PBXGroup; + children = ( + 8070C34BD15568D6408180CA44ECB25D /* CapacitorCordova.h */, + B0DB5BC19FEC623F1CBE34C0199ECEDF /* CDV.h */, + 0E6F9161A8CCA1441E8469B038FE571F /* CDVAvailability.h */, + E47F2F9A3113BDA943C95B07AB7ED996 /* CDVCommandDelegate.h */, + A8EEC7D6E5AD006BE212D95EF5C9BD38 /* CDVCommandDelegateImpl.h */, + B4CB6A2A62642506A01015CE92E3130D /* CDVCommandDelegateImpl.m */, + 04342480E00183EB8B7DF0F04924BD14 /* CDVInvokedUrlCommand.h */, + D9A050690A26CF7F3C24B4E759787DB5 /* CDVInvokedUrlCommand.m */, + 4CB2B21928DA7C37D4754B267DF0EA3D /* CDVPlugin.h */, + F6448305DFF5F42C21664CC58AD8BDA2 /* CDVPlugin.m */, + A79C4821C18E2A369BA6455D8603438E /* CDVPluginResult.h */, + FC1FDD8808DEE290ECBCA2118240BF2A /* CDVPluginResult.m */, + DFCD479AB516039656E9F73FDC576B7A /* Support Files */, + ); + name = CapacitorCordova; + path = CapacitorCordova; + sourceTree = ""; + }; + D978EF995AB434D7172648A0960C0E7C /* Core */ = { + isa = PBXGroup; + children = ( + 04049DDC3199BED1378FF9A3792F1503 /* GCDWebServer.h */, + AF3336C40A175E543EEA23581AAFE2E3 /* GCDWebServer.m */, + 0D378C9779249975BC5502F824BB89D6 /* GCDWebServerConnection.h */, + B4952B06145AE4353FFC75980CAA95C6 /* GCDWebServerConnection.m */, + 238A87C710B2E382669EF89E7BD90B4A /* GCDWebServerDataRequest.h */, + 108E16699B2DEF9AE07F4AEF5869B5F5 /* GCDWebServerDataRequest.m */, + E924A2F0AEFB4D1081CC0CBD1EBEB6C1 /* GCDWebServerDataResponse.h */, + A29E4BDC8AE640DD7FDF3490470F4C69 /* GCDWebServerDataResponse.m */, + 4A98051B2698EA6C7EAB9472FF42403D /* GCDWebServerErrorResponse.h */, + 7108676AEBBFAE3FBCE594D7CC6C117C /* GCDWebServerErrorResponse.m */, + E3B22768445D10142B0DDA3F75AC0812 /* GCDWebServerFileRequest.h */, + CA990B810AF10F64B436AD0B84D41796 /* GCDWebServerFileRequest.m */, + BBB6E9ABF35760779A4FB444BF92A1C6 /* GCDWebServerFileResponse.h */, + D5A38E9892C04A876C0164E71474A36C /* GCDWebServerFileResponse.m */, + 82931A51A703F596E4D0DEEEDAB480DE /* GCDWebServerFunctions.h */, + A59F65DEE3B97CADB06AFD91E58A6FFF /* GCDWebServerFunctions.m */, + AE78DEC8F31FAC6412386627F406AB18 /* GCDWebServerHTTPStatusCodes.h */, + EF743E3EFC44782809FD45EBBB3C5D7B /* GCDWebServerMultiPartFormRequest.h */, + AC0C427DC9FE1666F936B93F02AF95A4 /* GCDWebServerMultiPartFormRequest.m */, + F4B5F4EB0D2EFF225ACF6A25DCD3B715 /* GCDWebServerPrivate.h */, + F9210BC97271D8E0DD8E23122AB217AF /* GCDWebServerRequest.h */, + C9F2F7377CE8409EC28F618B4CEEB6AE /* GCDWebServerRequest.m */, + 2EE85AA0BE3B16D2640143DD9F189D32 /* GCDWebServerResponse.h */, + 04E3587CEB5BF4FAF5826471F7BC556A /* GCDWebServerResponse.m */, + EE85B786E92578FD3AB175811675C60E /* GCDWebServerStreamedResponse.h */, + 0C3BD6C463BE375744D2A29BA6DDB76D /* GCDWebServerStreamedResponse.m */, + 887CDCB2893FCBC4C7FF0F38E6DCD1DE /* GCDWebServerURLEncodedFormRequest.h */, + D8603AA4EB45CA3564F764F2A9A0FC33 /* GCDWebServerURLEncodedFormRequest.m */, + ); + name = Core; + sourceTree = ""; + }; + DEF95E85DA0C96A28DEC7A0574C21995 /* iOS */ = { + isa = PBXGroup; + children = ( + 2E6FD2397BDC6E76D6C81299E796BEB1 /* CFNetwork.framework */, + ED7824BB651B215467415382C9FD7D74 /* Foundation.framework */, + 40919191DFA7768380316D30CDCE8676 /* MobileCoreServices.framework */, + 27A2B496D8EF37BCD5B0AA6CCF40534E /* WebKit.framework */, + ); + name = iOS; + sourceTree = ""; + }; + DFCD479AB516039656E9F73FDC576B7A /* Support Files */ = { + isa = PBXGroup; + children = ( + 4A184D30FC6C2616301DC1C8DB173FE8 /* CapacitorCordova.modulemap */, + 23844A0F1A89B14D34F94A9E0FBCD2F0 /* CapacitorCordova.xcconfig */, + 6CDAE721290D5B06658B72653632BA75 /* CapacitorCordova-dummy.m */, + 918EDBE7786E4237E3CBDDD3A07CBB5C /* CapacitorCordova-prefix.pch */, + 49DB4966E0F97B7B96357A69D700F9B9 /* CapacitorCordova-umbrella.h */, + C00EFC7A7479E8544813C1C8FD7C78C9 /* Info.plist */, + ); + name = "Support Files"; + path = "../Target Support Files/CapacitorCordova"; + sourceTree = ""; + }; + F4368CB138A433FA424575144A309188 /* Pods-PluginTests */ = { + isa = PBXGroup; + children = ( + 44B274659DA6D8A45BA6133CBF6D5DE9 /* Info.plist */, + 1BB25D85E54AB05ED1BD1803AC9D7348 /* Pods-PluginTests.modulemap */, + AD2AF8B23415268D1796D3C648978A10 /* Pods-PluginTests-acknowledgements.markdown */, + 607748F18E8B9B53DA5F1870DEC05003 /* Pods-PluginTests-acknowledgements.plist */, + 62492EEBA85C43145E6425D5017914C1 /* Pods-PluginTests-dummy.m */, + CBBDAC4B05EFB43667A5A8195065B281 /* Pods-PluginTests-frameworks.sh */, + F688B7298929A01E6891D1A8B343651E /* Pods-PluginTests-resources.sh */, + 327F5533726B8C7DAA70FB982C097983 /* Pods-PluginTests-umbrella.h */, + FAADC53959C773CBD73D65D23C6797FB /* Pods-PluginTests.debug.xcconfig */, + B9D773079AF0ACA53F33A135CEF3FDA9 /* Pods-PluginTests.release.xcconfig */, + ); + name = "Pods-PluginTests"; + path = "Target Support Files/Pods-PluginTests"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 2C99C5C966840494FD7F68E6F646A0B2 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 20403F4DE5E6B41098E276FDF2E91E8F /* CapacitorCordova-umbrella.h in Headers */, + 81BA50EF46280A6C468244D6CC6DC1C6 /* CapacitorCordova.h in Headers */, + 6D17D5CAC54227E1DAE04F3BE7DD20B8 /* CDV.h in Headers */, + 15E0EE338DA0037C05F6AAA61C83274D /* CDVAvailability.h in Headers */, + 9CF024FDB0468F5C2DDF06EA5E3F3A2B /* CDVCommandDelegate.h in Headers */, + 9426B26DA4DBFB33D748A0F243AB04DC /* CDVCommandDelegateImpl.h in Headers */, + 78938767D0EA7F0B7F053570738E834C /* CDVInvokedUrlCommand.h in Headers */, + 8D7B7C6B41A3F6B9A1E1BA5134FAB428 /* CDVPlugin.h in Headers */, + 6E21D9576D486B45EA11E4FF703780A8 /* CDVPluginResult.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 40DA0676391A67C534AAFCDC4F4FBE06 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + FAA7816DF46AF9C96F037CC794177B7E /* Capacitor-umbrella.h in Headers */, + 46920A032FED74A322E909D4D01F07CF /* Capacitor.h in Headers */, + A50D54AA115920DAD039EAE4FEFDF868 /* CAPBridgedPlugin.h in Headers */, + 1DDA06F0C75E5A21ACCBFF1F36BDC87F /* CAPPlugin.h in Headers */, + 6C40FC1FBE78F4640335C07F540B96C9 /* CAPPluginCall.h in Headers */, + 560734159E292543F01623E8EF30F6FC /* CAPPluginMethod.h in Headers */, + EFC9A030D2F6AD974C166A6D2A6C4DCD /* DefaultPlugins.h in Headers */, + 6E7F25F09B84381BBA36271087ED4BED /* Keyboard.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BD2EDF8652CDF8AE03724222324FC001 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 529ED6BDD27B4EB3B874042C436F1973 /* Pods-Plugin-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BF6FC1B62F853D9059C38F630C922D50 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 39C0034EA5999011D40A4CD49BE3DE4B /* Pods-PluginTests-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D80513535B1E16EEA0AF4E07D509674B /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 7B3F12E3B58ED7028EE4B29BD83F8D3A /* GCDWebServer-umbrella.h in Headers */, + 44395BD8EB08535F9A73096B004906A3 /* GCDWebServer.h in Headers */, + 76EAC3CBDD4A45CF9081E75E67F0593D /* GCDWebServerConnection.h in Headers */, + 0DC0A2F86F02A337CD17736BA5DB8319 /* GCDWebServerDataRequest.h in Headers */, + D90ECA4AAA4A402BCD1C517F37D0915A /* GCDWebServerDataResponse.h in Headers */, + 7F999640850C5FDBDE12791E39D4D884 /* GCDWebServerErrorResponse.h in Headers */, + 7AF590661E39B6828A3192E4C61B4B65 /* GCDWebServerFileRequest.h in Headers */, + 68DA69B4293D2F46B37A19D4E3995308 /* GCDWebServerFileResponse.h in Headers */, + 3BEB4AB2C590B15F4E8FD6A39244DCEA /* GCDWebServerFunctions.h in Headers */, + 043E6E9889071AAC35E5E72CCC26838E /* GCDWebServerHTTPStatusCodes.h in Headers */, + 09EB1BF1776A906E5F882FC2EC132C92 /* GCDWebServerMultiPartFormRequest.h in Headers */, + DB995C36E84724AF729FA6B62DA1F6C1 /* GCDWebServerPrivate.h in Headers */, + 0C73050F432F69AE6F8CDD5733282C00 /* GCDWebServerRequest.h in Headers */, + 6338DE8191573141F7FFD7B864E6F3B8 /* GCDWebServerResponse.h in Headers */, + 49841AD16EB4501EB30C9A41002FF939 /* GCDWebServerStreamedResponse.h in Headers */, + BF4690A69C1B90064044C2B2B29D25A7 /* GCDWebServerURLEncodedFormRequest.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 0D6BF964562CED21242461655F87C79A /* GCDWebServer */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0851F33CC44DF9358999C4E1CB6FEDEB /* Build configuration list for PBXNativeTarget "GCDWebServer" */; + buildPhases = ( + 0A3168AE1690D51452E5637D09053583 /* Sources */, + 636258FD9AA272C1BCF942157BED9F72 /* Frameworks */, + D80513535B1E16EEA0AF4E07D509674B /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = GCDWebServer; + productName = GCDWebServer; + productReference = 438F88C054F05BB7899CB0A42E0E88D6 /* GCDWebServer.framework */; + productType = "com.apple.product-type.framework"; + }; + CE4A2CEA40367A66C221815BF8D739BD /* Pods-PluginTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 75137CB7E6121B0BE1E95B37DB201377 /* Build configuration list for PBXNativeTarget "Pods-PluginTests" */; + buildPhases = ( + 13EC7A6051C9AB5EC675EDBE804CC011 /* Sources */, + E0E4E15EC3295DA3D09CAFC72BA4263B /* Frameworks */, + BF6FC1B62F853D9059C38F630C922D50 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + 252BF9FF99B5C63DE2DB2E7D973A96E6 /* PBXTargetDependency */, + C9019F1A9C5F028D963089BA89B0984D /* PBXTargetDependency */, + C649D59FD1029B47A7CF9571E12EE01F /* PBXTargetDependency */, + ); + name = "Pods-PluginTests"; + productName = "Pods-PluginTests"; + productReference = D9BBAF6C6ED8BF5E4B9F8B042C22591D /* Pods_PluginTests.framework */; + productType = "com.apple.product-type.framework"; + }; + ECB48E98E0984198BBC4F65095530F94 /* Capacitor */ = { + isa = PBXNativeTarget; + buildConfigurationList = 19093BCB2DFED8D6E18AB0803FA19331 /* Build configuration list for PBXNativeTarget "Capacitor" */; + buildPhases = ( + 9E596A2A38A91C39B606353B580AF8A3 /* Sources */, + FD91981E66CFF57D6FFD3C86B424B5C7 /* Frameworks */, + 40DA0676391A67C534AAFCDC4F4FBE06 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + 4B226E65EEC33B7D6AF85CE5603097AB /* PBXTargetDependency */, + 1F70D3D8E234B979484216B4BF32EA40 /* PBXTargetDependency */, + ); + name = Capacitor; + productName = Capacitor; + productReference = B829A9F4584637BB0C6A4D2472881165 /* Capacitor.framework */; + productType = "com.apple.product-type.framework"; + }; + F2C6209A9A06614635020AABB22C60A2 /* CapacitorCordova */ = { + isa = PBXNativeTarget; + buildConfigurationList = B18AE4C3361A72E5C71A91C3BF38CB03 /* Build configuration list for PBXNativeTarget "CapacitorCordova" */; + buildPhases = ( + 81C33D82339AC183F82B278B4411FA30 /* Sources */, + EA987A8F80A00AFCA0DEC80FAE85FDF3 /* Frameworks */, + 2C99C5C966840494FD7F68E6F646A0B2 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CapacitorCordova; + productName = CapacitorCordova; + productReference = BD4AE95D50875216D1A98BB5546A39C5 /* Cordova.framework */; + productType = "com.apple.product-type.framework"; + }; + F44ACBE3595F9E5A6E0BF510C8247099 /* Pods-Plugin */ = { + isa = PBXNativeTarget; + buildConfigurationList = 284EE32250FE45FAAFF3BA9611ED6CB9 /* Build configuration list for PBXNativeTarget "Pods-Plugin" */; + buildPhases = ( + 59481836353C6AD4ED7902E4FB468F1F /* Sources */, + 21BDA92483318B212FC10E7EC1883359 /* Frameworks */, + BD2EDF8652CDF8AE03724222324FC001 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + C7867E97A07BC7849DCF3C422FB19308 /* PBXTargetDependency */, + DB462E972F33FDA39D5158E84118F6C8 /* PBXTargetDependency */, + E59403EC3CB5166F36020CCBB82C709F /* PBXTargetDependency */, + ); + name = "Pods-Plugin"; + productName = "Pods-Plugin"; + productReference = 5D0442A24B87493D3D03D346D8B245B6 /* Pods_Plugin.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 0700; + }; + buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 7DB346D0F39D3F0E887471402A8071AB; + productRefGroup = 828DC58EE2DE2C4AB33FE717A10939A2 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + ECB48E98E0984198BBC4F65095530F94 /* Capacitor */, + F2C6209A9A06614635020AABB22C60A2 /* CapacitorCordova */, + 0D6BF964562CED21242461655F87C79A /* GCDWebServer */, + F44ACBE3595F9E5A6E0BF510C8247099 /* Pods-Plugin */, + CE4A2CEA40367A66C221815BF8D739BD /* Pods-PluginTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 0A3168AE1690D51452E5637D09053583 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2B507CF8B7556050B28441172CA1DA75 /* GCDWebServer-dummy.m in Sources */, + 8EB4782A89D9AFD7D43D6B4F20736F57 /* GCDWebServer.m in Sources */, + 7ED4D5453E24ED78A3472B1C3CD97872 /* GCDWebServerConnection.m in Sources */, + 708AD5084381D27FF7982E2E36128925 /* GCDWebServerDataRequest.m in Sources */, + 960DDC45908F39283323BD09994A9434 /* GCDWebServerDataResponse.m in Sources */, + 87098BA586B1F09D72F014493C050550 /* GCDWebServerErrorResponse.m in Sources */, + 6FC75817C8971D747DC663F012C893E0 /* GCDWebServerFileRequest.m in Sources */, + 6BAB40AA93ADB79B575494EDBA8F0BE3 /* GCDWebServerFileResponse.m in Sources */, + C1E54E25CB130740EFA7C711B06EFB73 /* GCDWebServerFunctions.m in Sources */, + 96DE6C36300A1B5B48040A32409FDBDB /* GCDWebServerMultiPartFormRequest.m in Sources */, + DB1E729BF4D02B3E687CF18686DD2FD6 /* GCDWebServerRequest.m in Sources */, + 8E2AC03CCB7B0B15CCECD4D469A86FC9 /* GCDWebServerResponse.m in Sources */, + 6F4D1476A9ADCB6392F88A1B7859AD8E /* GCDWebServerStreamedResponse.m in Sources */, + A8430ADDE070336A355BC1C96068738A /* GCDWebServerURLEncodedFormRequest.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13EC7A6051C9AB5EC675EDBE804CC011 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2F893645A50B732A9A36933230D56CA2 /* Pods-PluginTests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 59481836353C6AD4ED7902E4FB468F1F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 09FE3A3409FC54A7DAE6C25E1879DDB7 /* Pods-Plugin-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 81C33D82339AC183F82B278B4411FA30 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 748D0E506BCB9164402093890274DED6 /* CapacitorCordova-dummy.m in Sources */, + 6E854AC59BE60B56AC936036E06B44E0 /* CDVCommandDelegateImpl.m in Sources */, + 6BBD2C94FA0A8FCF85C644F3DCFC6B80 /* CDVInvokedUrlCommand.m in Sources */, + 2C2DC089098BC4886AAF7850A1F7B77B /* CDVPlugin.m in Sources */, + 44565DD3D720CA7A3345A545F177C67B /* CDVPluginResult.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9E596A2A38A91C39B606353B580AF8A3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8E19C32F6247A29B39590464B1572B20 /* Accessibility.swift in Sources */, + 5F0C483D1D41FDBBFD344CA5A2EDCAC7 /* App.swift in Sources */, + 9C14F4B190D99DCAA09E4CC70468C34C /* Browser.swift in Sources */, + 6F135094028F0A6E46F57BFDE9B42266 /* Camera.swift in Sources */, + D3B2C6EAD0A87BCE0478E985A21125CD /* Capacitor-dummy.m in Sources */, + 987EB65A3AF1FF11F07D2AB1253039AA /* CAPBridge.swift in Sources */, + C51CC67DE4D57F92667322D20468E197 /* CAPBridgedPlugin.m in Sources */, + B6C9E6799DAB16CF49183936BC21A4B0 /* CAPBridgeViewController.swift in Sources */, + 6CAAE99014BD7E6F44D9860D44FCF9C8 /* CAPFile.swift in Sources */, + 5A06C00862B8DBE9C1C9200013BD88A8 /* CAPNotifications.swift in Sources */, + 3D65F0D0A48C244A07E13BFF1839CFD6 /* CAPPlugin.m in Sources */, + 57F6D06F5F2D74F654190A8813F811F0 /* CAPPluginCall.m in Sources */, + 0B5081DF930592B7F7C438C068ED482E /* CAPPluginCall.swift in Sources */, + 1DA33F4C5137B8CA2DD3AE44EC1F0631 /* CAPPluginMethod.m in Sources */, + 21F21A4F2FCB403EF0DE3FAB3F49AFF8 /* Clipboard.swift in Sources */, + DBA11829B9248732CD4542D884C55CA4 /* Console.swift in Sources */, + 51303982C97E58544753A8EB2EE9C474 /* DefaultPlugins.m in Sources */, + 10F0E288C848B9198AF9F458E6C83954 /* Device.swift in Sources */, + 1BF0170BA802C0ED323E504461BACCF0 /* DevMode.swift in Sources */, + 1BC48A6CF72447C92CC2FEA464C26EE7 /* Diagnostics.swift in Sources */, + 6CA2DD4AE85905D9E45A50955FC047B5 /* DocLinks.swift in Sources */, + 9082666CACBC16B3CC555BD6FF7CFC81 /* Filesystem.swift in Sources */, + CD065EB5F8347DB027A249C99D7117C4 /* Geolocation.swift in Sources */, + CCF6EAC8B78069231C94691365FA4F57 /* Haptics.swift in Sources */, + 2F3EFB912B078E5BEBD5C6F956D9CAA2 /* JS.swift in Sources */, + 2F3DBAEA8252E28C952DA191C848FF4D /* JSExport.swift in Sources */, + 3FFD4DFDB81169A3CB7069C5D2E5FCF5 /* Keyboard.m in Sources */, + DBB67EB853BB43E3D122CA5465AB0DE6 /* LocalNotifications.swift in Sources */, + 77FF3D25D6B1FA613EF83AA41178B2C7 /* Modals.swift in Sources */, + 0F288F27D3A53E980FEBA303603ADF9D /* Network.swift in Sources */, + AF75C7CE199061BEE9387CA3AF930412 /* Photos.swift in Sources */, + 6CB900534DF395C6601A1F8B8174DE7C /* PushNotifications.swift in Sources */, + 8D7549946638DE6F6BDAE007BB0DB955 /* Reachability.swift in Sources */, + 275DBDE456DF7FDAFA2E252F5D136E9E /* Share.swift in Sources */, + E943E0AF33B124F40CFDC464F2249C11 /* SplashScreen.swift in Sources */, + FC0A97AF36AF3E76BE2698C867FA0C89 /* StatusBar.swift in Sources */, + 4058FB98EED742291A20F1FE76551465 /* Toast.swift in Sources */, + 60A3229749A6FA08ADB00363BB8EB9BF /* UIColor.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 1F70D3D8E234B979484216B4BF32EA40 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = GCDWebServer; + target = 0D6BF964562CED21242461655F87C79A /* GCDWebServer */; + targetProxy = 19E99F21FE7682C87EC8433510CB98F1 /* PBXContainerItemProxy */; + }; + 252BF9FF99B5C63DE2DB2E7D973A96E6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Capacitor; + target = ECB48E98E0984198BBC4F65095530F94 /* Capacitor */; + targetProxy = 7A10114D4601FFC9F0865ECA596CAADD /* PBXContainerItemProxy */; + }; + 4B226E65EEC33B7D6AF85CE5603097AB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = CapacitorCordova; + target = F2C6209A9A06614635020AABB22C60A2 /* CapacitorCordova */; + targetProxy = 3D4C7DB4A3B64685AD2AA89117BC60F6 /* PBXContainerItemProxy */; + }; + C649D59FD1029B47A7CF9571E12EE01F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = GCDWebServer; + target = 0D6BF964562CED21242461655F87C79A /* GCDWebServer */; + targetProxy = CB1E80C9C57203C2F44F2A00B3DC250B /* PBXContainerItemProxy */; + }; + C7867E97A07BC7849DCF3C422FB19308 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Capacitor; + target = ECB48E98E0984198BBC4F65095530F94 /* Capacitor */; + targetProxy = 37107F2799C1A363FCE4F115333D56F0 /* PBXContainerItemProxy */; + }; + C9019F1A9C5F028D963089BA89B0984D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = CapacitorCordova; + target = F2C6209A9A06614635020AABB22C60A2 /* CapacitorCordova */; + targetProxy = 4E1AA4BC1611389AC010AB84C698F9F2 /* PBXContainerItemProxy */; + }; + DB462E972F33FDA39D5158E84118F6C8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = CapacitorCordova; + target = F2C6209A9A06614635020AABB22C60A2 /* CapacitorCordova */; + targetProxy = 09A6C757E3A045EB962DED1B709C7895 /* PBXContainerItemProxy */; + }; + E59403EC3CB5166F36020CCBB82C709F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = GCDWebServer; + target = 0D6BF964562CED21242461655F87C79A /* GCDWebServer */; + targetProxy = EF86EF2C55C8C2D75C8377A435F99F5C /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 01FFE380FD723623B008D318CAB13137 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 23844A0F1A89B14D34F94A9E0FBCD2F0 /* CapacitorCordova.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/CapacitorCordova/CapacitorCordova-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/CapacitorCordova/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/CapacitorCordova/CapacitorCordova.modulemap"; + PRODUCT_NAME = Cordova; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 0ABCECE343EDC2D38344FFD7ECDB53DD /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0F243C94C04E9548EAFE09CC7AF16922 /* Capacitor.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Capacitor/Capacitor-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Capacitor/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Capacitor/Capacitor.modulemap"; + PRODUCT_NAME = Capacitor; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 3E75FF95B0F665873D35199DF341EF9A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C472A9B605FF956581FD5542255434D6 /* GCDWebServer.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/GCDWebServer/GCDWebServer-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/GCDWebServer/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/GCDWebServer/GCDWebServer.modulemap"; + PRODUCT_NAME = GCDWebServer; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 7C07ED8089F70A31B83996A8910D7F18 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + 97828C9CA1CD7D1962630D6BF59ACA77 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B9D773079AF0ACA53F33A135CEF3FDA9 /* Pods-PluginTests.release.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-PluginTests/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-PluginTests/Pods-PluginTests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = Pods_PluginTests; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 98F29E7567052F62660DDD7069ADF73C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Release; + }; + CE28FC17D428FFA7EDCA5F1810246A8F /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0F243C94C04E9548EAFE09CC7AF16922 /* Capacitor.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Capacitor/Capacitor-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Capacitor/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Capacitor/Capacitor.modulemap"; + PRODUCT_NAME = Capacitor; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + E13E2395643E4B18F842C2A1FD5AD5C9 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 23844A0F1A89B14D34F94A9E0FBCD2F0 /* CapacitorCordova.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/CapacitorCordova/CapacitorCordova-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/CapacitorCordova/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/CapacitorCordova/CapacitorCordova.modulemap"; + PRODUCT_NAME = Cordova; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + E335D02A3DB808D37D0DB44EA3DBA8FE /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B976A28B6CF4200AC9E2ED1197803AC6 /* Pods-Plugin.debug.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-Plugin/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Plugin/Pods-Plugin.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = Pods_Plugin; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + E3AD9A1445E1DD8F3215568BA05447A3 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FAADC53959C773CBD73D65D23C6797FB /* Pods-PluginTests.debug.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-PluginTests/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-PluginTests/Pods-PluginTests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = Pods_PluginTests; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + F98927429A7CFB2413BAE714EA01A3D4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 871AF3084993B6A24A46CEA3C4ACE39E /* Pods-Plugin.release.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-Plugin/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Plugin/Pods-Plugin.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = Pods_Plugin; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + FD4315DCD1288264D2D723D5CCAFFCB7 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C472A9B605FF956581FD5542255434D6 /* GCDWebServer.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/GCDWebServer/GCDWebServer-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/GCDWebServer/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/GCDWebServer/GCDWebServer.modulemap"; + PRODUCT_NAME = GCDWebServer; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0851F33CC44DF9358999C4E1CB6FEDEB /* Build configuration list for PBXNativeTarget "GCDWebServer" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FD4315DCD1288264D2D723D5CCAFFCB7 /* Debug */, + 3E75FF95B0F665873D35199DF341EF9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 19093BCB2DFED8D6E18AB0803FA19331 /* Build configuration list for PBXNativeTarget "Capacitor" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0ABCECE343EDC2D38344FFD7ECDB53DD /* Debug */, + CE28FC17D428FFA7EDCA5F1810246A8F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 284EE32250FE45FAAFF3BA9611ED6CB9 /* Build configuration list for PBXNativeTarget "Pods-Plugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E335D02A3DB808D37D0DB44EA3DBA8FE /* Debug */, + F98927429A7CFB2413BAE714EA01A3D4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7C07ED8089F70A31B83996A8910D7F18 /* Debug */, + 98F29E7567052F62660DDD7069ADF73C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 75137CB7E6121B0BE1E95B37DB201377 /* Build configuration list for PBXNativeTarget "Pods-PluginTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E3AD9A1445E1DD8F3215568BA05447A3 /* Debug */, + 97828C9CA1CD7D1962630D6BF59ACA77 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B18AE4C3361A72E5C71A91C3BF38CB03 /* Build configuration list for PBXNativeTarget "CapacitorCordova" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E13E2395643E4B18F842C2A1FD5AD5C9 /* Debug */, + 01FFE380FD723623B008D318CAB13137 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; +} diff --git a/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/Capacitor.xcscheme b/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/Capacitor.xcscheme new file mode 100644 index 0000000..38ca7a5 --- /dev/null +++ b/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/Capacitor.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/CapacitorCordova.xcscheme b/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/CapacitorCordova.xcscheme new file mode 100644 index 0000000..457bce8 --- /dev/null +++ b/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/CapacitorCordova.xcscheme @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/GCDWebServer.xcscheme b/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/GCDWebServer.xcscheme new file mode 100644 index 0000000..002efb5 --- /dev/null +++ b/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/GCDWebServer.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/Pods-Plugin.xcscheme b/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/Pods-Plugin.xcscheme new file mode 100644 index 0000000..5de2e87 --- /dev/null +++ b/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/Pods-Plugin.xcscheme @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/Pods-PluginTests.xcscheme b/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/Pods-PluginTests.xcscheme new file mode 100644 index 0000000..195997b --- /dev/null +++ b/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/Pods-PluginTests.xcscheme @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/xcschememanagement.plist b/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..f784796 --- /dev/null +++ b/ios/Plugin/Pods/Pods.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,46 @@ + + + + + SchemeUserState + + Capacitor.xcscheme + + isShown + + orderHint + 0 + + CapacitorCordova.xcscheme + + isShown + + orderHint + 1 + + GCDWebServer.xcscheme + + isShown + + orderHint + 2 + + Pods-Plugin.xcscheme + + isShown + + orderHint + 3 + + Pods-PluginTests.xcscheme + + isShown + + orderHint + 4 + + + SuppressBuildableAutocreation + + + diff --git a/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor-dummy.m b/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor-dummy.m new file mode 100644 index 0000000..be1a844 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Capacitor : NSObject +@end +@implementation PodsDummy_Capacitor +@end diff --git a/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor-prefix.pch b/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor-umbrella.h b/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor-umbrella.h new file mode 100644 index 0000000..ebe4210 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor-umbrella.h @@ -0,0 +1,23 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "Capacitor.h" +#import "CAPBridgedPlugin.h" +#import "CAPPlugin.h" +#import "CAPPluginCall.h" +#import "CAPPluginMethod.h" +#import "DefaultPlugins.h" +#import "Keyboard.h" + +FOUNDATION_EXPORT double CapacitorVersionNumber; +FOUNDATION_EXPORT const unsigned char CapacitorVersionString[]; + diff --git a/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor.modulemap b/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor.modulemap new file mode 100644 index 0000000..bfab4ef --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor.modulemap @@ -0,0 +1,6 @@ +framework module Capacitor { + umbrella header "Capacitor-umbrella.h" + + export * + module * { export * } +} diff --git a/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor.xcconfig b/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor.xcconfig new file mode 100644 index 0000000..01fb0fc --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Capacitor/Capacitor.xcconfig @@ -0,0 +1,11 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Capacitor +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/CapacitorCordova" "${PODS_CONFIGURATION_BUILD_DIR}/GCDWebServer" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Capacitor +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/ios/Plugin/Pods/Target Support Files/Capacitor/Info.plist b/ios/Plugin/Pods/Target Support Files/Capacitor/Info.plist new file mode 100644 index 0000000..40c9f60 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Capacitor/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 0.0.8 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova-dummy.m b/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova-dummy.m new file mode 100644 index 0000000..47e9c39 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_CapacitorCordova : NSObject +@end +@implementation PodsDummy_CapacitorCordova +@end diff --git a/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova-prefix.pch b/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova-umbrella.h b/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova-umbrella.h new file mode 100644 index 0000000..13b046b --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova-umbrella.h @@ -0,0 +1,23 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "CDV.h" +#import "CDVAvailability.h" +#import "CDVCommandDelegate.h" +#import "CDVCommandDelegateImpl.h" +#import "CDVInvokedUrlCommand.h" +#import "CDVPlugin.h" +#import "CDVPluginResult.h" + +FOUNDATION_EXPORT double CordovaVersionNumber; +FOUNDATION_EXPORT const unsigned char CordovaVersionString[]; + diff --git a/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova.modulemap b/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova.modulemap new file mode 100644 index 0000000..3dc1b4f --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova.modulemap @@ -0,0 +1,6 @@ +framework module Cordova { + umbrella header "CapacitorCordova-umbrella.h" + + export * + module * { export * } +} diff --git a/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova.xcconfig b/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova.xcconfig new file mode 100644 index 0000000..8d17193 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/CapacitorCordova/CapacitorCordova.xcconfig @@ -0,0 +1,10 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/CapacitorCordova +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" +OTHER_LDFLAGS = -framework "WebKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/CapacitorCordova +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/ios/Plugin/Pods/Target Support Files/CapacitorCordova/Info.plist b/ios/Plugin/Pods/Target Support Files/CapacitorCordova/Info.plist new file mode 100644 index 0000000..40c9f60 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/CapacitorCordova/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 0.0.8 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer-dummy.m b/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer-dummy.m new file mode 100644 index 0000000..4cb3a83 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_GCDWebServer : NSObject +@end +@implementation PodsDummy_GCDWebServer +@end diff --git a/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer-prefix.pch b/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer-umbrella.h b/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer-umbrella.h new file mode 100644 index 0000000..845fa78 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer-umbrella.h @@ -0,0 +1,30 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "GCDWebServer.h" +#import "GCDWebServerConnection.h" +#import "GCDWebServerFunctions.h" +#import "GCDWebServerHTTPStatusCodes.h" +#import "GCDWebServerRequest.h" +#import "GCDWebServerResponse.h" +#import "GCDWebServerDataRequest.h" +#import "GCDWebServerFileRequest.h" +#import "GCDWebServerMultiPartFormRequest.h" +#import "GCDWebServerURLEncodedFormRequest.h" +#import "GCDWebServerDataResponse.h" +#import "GCDWebServerErrorResponse.h" +#import "GCDWebServerFileResponse.h" +#import "GCDWebServerStreamedResponse.h" + +FOUNDATION_EXPORT double GCDWebServerVersionNumber; +FOUNDATION_EXPORT const unsigned char GCDWebServerVersionString[]; + diff --git a/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer.modulemap b/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer.modulemap new file mode 100644 index 0000000..a5af576 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer.modulemap @@ -0,0 +1,6 @@ +framework module GCDWebServer { + umbrella header "GCDWebServer-umbrella.h" + + export * + module * { export * } +} diff --git a/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer.xcconfig b/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer.xcconfig new file mode 100644 index 0000000..fe4622f --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/GCDWebServer/GCDWebServer.xcconfig @@ -0,0 +1,10 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/GCDWebServer +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" +OTHER_LDFLAGS = -l"z" -framework "CFNetwork" -framework "MobileCoreServices" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/GCDWebServer +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/ios/Plugin/Pods/Target Support Files/GCDWebServer/Info.plist b/ios/Plugin/Pods/Target Support Files/GCDWebServer/Info.plist new file mode 100644 index 0000000..152c333 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/GCDWebServer/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 3.4.2 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Info.plist b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Info.plist new file mode 100644 index 0000000..2243fe6 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-acknowledgements.markdown b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-acknowledgements.markdown new file mode 100644 index 0000000..70eec1e --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-acknowledgements.markdown @@ -0,0 +1,85 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## Capacitor + +Copyright 2015-present Drifty Co. +http://drifty.com/ + +MIT License + +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: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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. + + +## CapacitorCordova + +Copyright 2015-present Drifty Co. +http://drifty.com/ + +MIT License + +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: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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. + + +## GCDWebServer + +Copyright (c) 2012-2014, Pierre-Olivier Latour +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Generated by CocoaPods - https://cocoapods.org diff --git a/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-acknowledgements.plist b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-acknowledgements.plist new file mode 100644 index 0000000..74c32f9 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-acknowledgements.plist @@ -0,0 +1,129 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Copyright 2015-present Drifty Co. +http://drifty.com/ + +MIT License + +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: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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. + + License + MIT + Title + Capacitor + Type + PSGroupSpecifier + + + FooterText + Copyright 2015-present Drifty Co. +http://drifty.com/ + +MIT License + +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: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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. + + License + MIT + Title + CapacitorCordova + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2012-2014, Pierre-Olivier Latour +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + License + BSD + Title + GCDWebServer + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-dummy.m b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-dummy.m new file mode 100644 index 0000000..8685bcb --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_Plugin : NSObject +@end +@implementation PodsDummy_Pods_Plugin +@end diff --git a/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-resources.sh b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-resources.sh new file mode 100755 index 0000000..a7df440 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-resources.sh @@ -0,0 +1,106 @@ +#!/bin/sh +set -e + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +case "${TARGETED_DEVICE_FAMILY}" in + 1,2) + TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" + ;; + 1) + TARGET_DEVICE_ARGS="--target-device iphone" + ;; + 2) + TARGET_DEVICE_ARGS="--target-device ipad" + ;; + 3) + TARGET_DEVICE_ARGS="--target-device tv" + ;; + 4) + TARGET_DEVICE_ARGS="--target-device watch" + ;; + *) + TARGET_DEVICE_ARGS="--target-device mac" + ;; +esac + +install_resource() +{ + if [[ "$1" = /* ]] ; then + RESOURCE_PATH="$1" + else + RESOURCE_PATH="${PODS_ROOT}/$1" + fi + if [[ ! -e "$RESOURCE_PATH" ]] ; then + cat << EOM +error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. +EOM + exit 1 + fi + case $RESOURCE_PATH in + *.storyboard) + 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 + 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} + ;; + *.xib) + 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 + 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} + ;; + *.framework) + echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true + mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + ;; + *.xcdatamodel) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" + ;; + *.xcdatamodeld) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" + ;; + *.xcmappingmodel) + echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true + xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" + ;; + *.xcassets) + ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" + XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") + ;; + *) + echo "$RESOURCE_PATH" || true + echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" + ;; + esac +} + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then + mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi +rm -f "$RESOURCES_TO_COPY" + +if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] +then + # Find all other xcassets (this unfortunately includes those of path pods and other targets). + OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) + while read line; do + if [[ $line != "${PODS_ROOT}*" ]]; then + XCASSET_FILES+=("$line") + fi + done <<<"$OTHER_XCASSETS" + + 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}" +fi diff --git a/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-umbrella.h b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-umbrella.h new file mode 100644 index 0000000..ce248b2 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_PluginVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_PluginVersionString[]; + diff --git a/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin.debug.xcconfig b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin.debug.xcconfig new file mode 100644 index 0000000..138d0a7 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin.debug.xcconfig @@ -0,0 +1,10 @@ +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Capacitor" "${PODS_CONFIGURATION_BUILD_DIR}/CapacitorCordova" "${PODS_CONFIGURATION_BUILD_DIR}/GCDWebServer" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' '@executable_path/../../Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Capacitor/Capacitor.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/CapacitorCordova/Cordova.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/GCDWebServer/GCDWebServer.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "Capacitor" -framework "Cordova" -framework "GCDWebServer" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin.modulemap b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin.modulemap new file mode 100644 index 0000000..6ee05ec --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin.modulemap @@ -0,0 +1,6 @@ +framework module Pods_Plugin { + umbrella header "Pods-Plugin-umbrella.h" + + export * + module * { export * } +} diff --git a/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin.release.xcconfig b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin.release.xcconfig new file mode 100644 index 0000000..138d0a7 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-Plugin/Pods-Plugin.release.xcconfig @@ -0,0 +1,10 @@ +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Capacitor" "${PODS_CONFIGURATION_BUILD_DIR}/CapacitorCordova" "${PODS_CONFIGURATION_BUILD_DIR}/GCDWebServer" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' '@executable_path/../../Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Capacitor/Capacitor.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/CapacitorCordova/Cordova.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/GCDWebServer/GCDWebServer.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "Capacitor" -framework "Cordova" -framework "GCDWebServer" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Info.plist b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Info.plist new file mode 100644 index 0000000..2243fe6 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-acknowledgements.markdown b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-acknowledgements.markdown new file mode 100644 index 0000000..70eec1e --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-acknowledgements.markdown @@ -0,0 +1,85 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## Capacitor + +Copyright 2015-present Drifty Co. +http://drifty.com/ + +MIT License + +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: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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. + + +## CapacitorCordova + +Copyright 2015-present Drifty Co. +http://drifty.com/ + +MIT License + +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: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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. + + +## GCDWebServer + +Copyright (c) 2012-2014, Pierre-Olivier Latour +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Generated by CocoaPods - https://cocoapods.org diff --git a/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-acknowledgements.plist b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-acknowledgements.plist new file mode 100644 index 0000000..74c32f9 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-acknowledgements.plist @@ -0,0 +1,129 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Copyright 2015-present Drifty Co. +http://drifty.com/ + +MIT License + +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: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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. + + License + MIT + Title + Capacitor + Type + PSGroupSpecifier + + + FooterText + Copyright 2015-present Drifty Co. +http://drifty.com/ + +MIT License + +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: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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. + + License + MIT + Title + CapacitorCordova + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2012-2014, Pierre-Olivier Latour +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Pierre-Olivier Latour may not be used to endorse + or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + License + BSD + Title + GCDWebServer + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-dummy.m b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-dummy.m new file mode 100644 index 0000000..fd42ff1 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_PluginTests : NSObject +@end +@implementation PodsDummy_Pods_PluginTests +@end diff --git a/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh new file mode 100755 index 0000000..05ea405 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh @@ -0,0 +1,148 @@ +#!/bin/sh +set -e + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} + +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + if [ -r "$source" ]; then + # Copy the dSYM into a the targets temp dir. + 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}\"" + 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}" + + local basename + basename="$(basename -s .framework.dSYM "$source")" + binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then + strip_invalid_archs "$binary" + fi + + if [[ $STRIP_BINARY_RETVAL == 1 ]]; then + # Move the stripped file into its final destination. + 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}\"" + 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}" + else + # 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. + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" + fi + fi +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identitiy + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + STRIP_BINARY_RETVAL=0 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" || exit 1 + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=1 +} + + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/Capacitor/Capacitor.framework" + install_framework "${BUILT_PRODUCTS_DIR}/CapacitorCordova/Cordova.framework" + install_framework "${BUILT_PRODUCTS_DIR}/GCDWebServer/GCDWebServer.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/Capacitor/Capacitor.framework" + install_framework "${BUILT_PRODUCTS_DIR}/CapacitorCordova/Cordova.framework" + install_framework "${BUILT_PRODUCTS_DIR}/GCDWebServer/GCDWebServer.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-resources.sh b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-resources.sh new file mode 100755 index 0000000..a7df440 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-resources.sh @@ -0,0 +1,106 @@ +#!/bin/sh +set -e + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +case "${TARGETED_DEVICE_FAMILY}" in + 1,2) + TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" + ;; + 1) + TARGET_DEVICE_ARGS="--target-device iphone" + ;; + 2) + TARGET_DEVICE_ARGS="--target-device ipad" + ;; + 3) + TARGET_DEVICE_ARGS="--target-device tv" + ;; + 4) + TARGET_DEVICE_ARGS="--target-device watch" + ;; + *) + TARGET_DEVICE_ARGS="--target-device mac" + ;; +esac + +install_resource() +{ + if [[ "$1" = /* ]] ; then + RESOURCE_PATH="$1" + else + RESOURCE_PATH="${PODS_ROOT}/$1" + fi + if [[ ! -e "$RESOURCE_PATH" ]] ; then + cat << EOM +error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. +EOM + exit 1 + fi + case $RESOURCE_PATH in + *.storyboard) + 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 + 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} + ;; + *.xib) + 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 + 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} + ;; + *.framework) + echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true + mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + ;; + *.xcdatamodel) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" + ;; + *.xcdatamodeld) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" + ;; + *.xcmappingmodel) + echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true + xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" + ;; + *.xcassets) + ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" + XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") + ;; + *) + echo "$RESOURCE_PATH" || true + echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" + ;; + esac +} + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then + mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi +rm -f "$RESOURCES_TO_COPY" + +if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] +then + # Find all other xcassets (this unfortunately includes those of path pods and other targets). + OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) + while read line; do + if [[ $line != "${PODS_ROOT}*" ]]; then + XCASSET_FILES+=("$line") + fi + done <<<"$OTHER_XCASSETS" + + 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}" +fi diff --git a/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-umbrella.h b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-umbrella.h new file mode 100644 index 0000000..cac4be7 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_PluginTestsVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_PluginTestsVersionString[]; + diff --git a/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.debug.xcconfig b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.debug.xcconfig new file mode 100644 index 0000000..bf133d5 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.debug.xcconfig @@ -0,0 +1,11 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Capacitor" "${PODS_CONFIGURATION_BUILD_DIR}/CapacitorCordova" "${PODS_CONFIGURATION_BUILD_DIR}/GCDWebServer" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Capacitor/Capacitor.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/CapacitorCordova/Cordova.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/GCDWebServer/GCDWebServer.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "Capacitor" -framework "Cordova" -framework "GCDWebServer" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.modulemap b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.modulemap new file mode 100644 index 0000000..9e56660 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.modulemap @@ -0,0 +1,6 @@ +framework module Pods_PluginTests { + umbrella header "Pods-PluginTests-umbrella.h" + + export * + module * { export * } +} diff --git a/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.release.xcconfig b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.release.xcconfig new file mode 100644 index 0000000..bf133d5 --- /dev/null +++ b/ios/Plugin/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.release.xcconfig @@ -0,0 +1,11 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Capacitor" "${PODS_CONFIGURATION_BUILD_DIR}/CapacitorCordova" "${PODS_CONFIGURATION_BUILD_DIR}/GCDWebServer" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Capacitor/Capacitor.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/CapacitorCordova/Cordova.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/GCDWebServer/GCDWebServer.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "Capacitor" -framework "Cordova" -framework "GCDWebServer" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/package.json b/package.json new file mode 100644 index 0000000..b2df7a9 --- /dev/null +++ b/package.json @@ -0,0 +1,47 @@ +{ + "name": "capacitor-fused-location", + "version": "0.0.1", + "description": "Provides acces to the fused location api by google play services", + "main": "dist/esm/index.js", + "types": "dist/esm/index.d.ts", + "scripts": { + "build": "npm run clean && tsc", + "clean": "rm -rf ./dist", + "watch": "tsc --watch", + "prepublishOnly": "npm run build" + }, + "author": "Johannes Normann Jensen", + "license": "MIT", + "dependencies": { + "@capacitor/core": "latest" + }, + "devDependencies": { + "typescript": "^2.6.2" + }, + "files": [ + "dist/", + "ios/", + "android/", + "CapacitorFusedLocation.podspec" + ], + "keywords": [ + "capacitor", + "plugin", + "native" + ], + "capacitor": { + "ios": { + "src": "ios" + }, + "android": { + "src": "android" + } + }, + "repository": { + "type": "git", + "url": "https://github.com/jonoj-team/capacitor-fused-location" + }, + "bugs": { + "url": "https://github.com/jonoj-team/capacitor-fused-location/issues" + } +} diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..906e8a1 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,14 @@ +import nodeResolve from 'rollup-plugin-node-resolve'; + +export default { + input: 'dist/esm/index.js', + output: { + file: 'dist/plugin.js', + format: 'iife', + name: 'capacitorPlugin', + sourcemap: true + }, + plugins: [ + nodeResolve() + ] +}; \ No newline at end of file diff --git a/src/definitions.ts b/src/definitions.ts new file mode 100644 index 0000000..20fb7b4 --- /dev/null +++ b/src/definitions.ts @@ -0,0 +1,9 @@ +declare global { + interface PluginRegistry { + FusedLocation?: FusedLocationPlugin; + } +} + +export interface FusedLocationPlugin { + echo(options: { value: string }): Promise<{value: string}>; +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..6181377 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,2 @@ +export * from './definitions'; +export * from './web'; \ No newline at end of file diff --git a/src/web.ts b/src/web.ts new file mode 100644 index 0000000..fc13c8c --- /dev/null +++ b/src/web.ts @@ -0,0 +1,20 @@ +import { WebPlugin } from '@capacitor/core'; +import { FusedLocationPlugin } from './definitions'; + +export class FusedLocationWeb extends WebPlugin implements FusedLocationPlugin { + constructor() { + super({ + name: 'FusedLocation', + platforms: ['web'] + }); + } + + async echo(options: { value: string }): Promise<{value: string}> { + console.log('ECHO', options); + return options; + } +} + +const FusedLocation = new FusedLocationWeb(); + +export { FusedLocation }; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..4377384 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "declaration": true, + "experimentalDecorators": true, + "lib": [ + "dom", + "es2015" + ], + "module": "es2015", + "moduleResolution": "node", + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "outDir": "dist/esm", + "sourceMap": true, + "target": "es2015" + }, + "files": [ + "src/index.ts" + ] +}