Commit 9eb256b5 authored by Wei Han's avatar Wei Han

code update

parent 010b196a
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
44006126336D83DB220E7214 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; 44006126336D83DB220E7214 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; };
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
AF643AF9AD2E6D7F8427E3F1 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6FF98AE45D9936432AFEC029 /* libPods-example.a */; }; F2581A09DD01A13570F68628 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D30A3A8F21F213E96F2C8F7 /* libPods-example.a */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
...@@ -19,11 +19,11 @@ ...@@ -19,11 +19,11 @@
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = example/Images.xcassets; sourceTree = "<group>"; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = example/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = "<group>"; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = "<group>"; };
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = example/PrivacyInfo.xcprivacy; sourceTree = "<group>"; }; 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = example/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
688F14F134731D6E3695DCD4 /* Pods-example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.release.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.release.xcconfig"; sourceTree = "<group>"; }; 2D30A3A8F21F213E96F2C8F7 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
6FF98AE45D9936432AFEC029 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 508BA7CC4A1A0B5A23D84649 /* Pods-example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.release.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.release.xcconfig"; sourceTree = "<group>"; };
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = example/AppDelegate.swift; sourceTree = "<group>"; }; 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = example/AppDelegate.swift; sourceTree = "<group>"; };
7C33988A67E3A60B09B56690 /* Pods-example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.debug.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.debug.xcconfig"; sourceTree = "<group>"; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = example/LaunchScreen.storyboard; sourceTree = "<group>"; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = example/LaunchScreen.storyboard; sourceTree = "<group>"; };
E7FE0B6CF3283929F0ED2217 /* Pods-example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.debug.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.debug.xcconfig"; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */ /* End PBXFileReference section */
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
AF643AF9AD2E6D7F8427E3F1 /* libPods-example.a in Frameworks */, F2581A09DD01A13570F68628 /* libPods-example.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
6FF98AE45D9936432AFEC029 /* libPods-example.a */, 2D30A3A8F21F213E96F2C8F7 /* libPods-example.a */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -92,8 +92,8 @@ ...@@ -92,8 +92,8 @@
BBD78D7AC51CEA395F1C20DB /* Pods */ = { BBD78D7AC51CEA395F1C20DB /* Pods */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E7FE0B6CF3283929F0ED2217 /* Pods-example.debug.xcconfig */, 7C33988A67E3A60B09B56690 /* Pods-example.debug.xcconfig */,
688F14F134731D6E3695DCD4 /* Pods-example.release.xcconfig */, 508BA7CC4A1A0B5A23D84649 /* Pods-example.release.xcconfig */,
); );
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -105,13 +105,13 @@ ...@@ -105,13 +105,13 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */;
buildPhases = ( buildPhases = (
73D6515E0B05E56E1C38DCEE /* [CP] Check Pods Manifest.lock */, D47CB7885794FE215FE8B238 /* [CP] Check Pods Manifest.lock */,
13B07F871A680F5B00A75B9A /* Sources */, 13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */, 13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
13DCD710B66171E18819239B /* [CP] Embed Pods Frameworks */, ABA4014BAFCE28774B9D5225 /* [CP] Embed Pods Frameworks */,
E355AFBEE78FE218C8B2F995 /* [CP] Copy Pods Resources */, 4B920070F73F4233D4D79AC3 /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
...@@ -183,7 +183,24 @@ ...@@ -183,7 +183,24 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
}; };
13DCD710B66171E18819239B /* [CP] Embed Pods Frameworks */ = { 4B920070F73F4233D4D79AC3 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n";
showEnvVarsInLog = 0;
};
ABA4014BAFCE28774B9D5225 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
...@@ -200,7 +217,7 @@ ...@@ -200,7 +217,7 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
73D6515E0B05E56E1C38DCEE /* [CP] Check Pods Manifest.lock */ = { D47CB7885794FE215FE8B238 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
...@@ -222,23 +239,6 @@ ...@@ -222,23 +239,6 @@
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"; 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; showEnvVarsInLog = 0;
}; };
E355AFBEE78FE218C8B2F995 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
...@@ -255,7 +255,7 @@ ...@@ -255,7 +255,7 @@
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = { 13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = E7FE0B6CF3283929F0ED2217 /* Pods-example.debug.xcconfig */; baseConfigurationReference = 7C33988A67E3A60B09B56690 /* Pods-example.debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
...@@ -283,7 +283,7 @@ ...@@ -283,7 +283,7 @@
}; };
13B07F951A680F5B00A75B9A /* Release */ = { 13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 688F14F134731D6E3695DCD4 /* Pods-example.release.xcconfig */; baseConfigurationReference = 508BA7CC4A1A0B5A23D84649 /* Pods-example.release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
......
...@@ -9,10 +9,11 @@ ...@@ -9,10 +9,11 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
2F8D09092E9605E200C764FF /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F8D09082E9605E200C764FF /* CoreGraphics.framework */; }; 2F8D09092E9605E200C764FF /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F8D09082E9605E200C764FF /* CoreGraphics.framework */; };
2F99420D2EA9C5F000622199 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F99420C2EA9C5F000622199 /* QuartzCore.framework */; }; 2F99420D2EA9C5F000622199 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F99420C2EA9C5F000622199 /* QuartzCore.framework */; };
2FAEF09A2E8B7BC80086EDA3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FAEF0992E8B7BC80086EDA3 /* UIKit.framework */; };
2FAEF09C2E8B7BD40086EDA3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FAEF09B2E8B7BD40086EDA3 /* Foundation.framework */; }; 2FAEF09C2E8B7BD40086EDA3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FAEF09B2E8B7BD40086EDA3 /* Foundation.framework */; };
2FB2922A2EAB04F400D9F164 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FB292292EAB04F400D9F164 /* AVFoundation.framework */; }; 2FB2922A2EAB04F400D9F164 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FB292292EAB04F400D9F164 /* AVFoundation.framework */; };
2FB975E02ED7F9AA00E64A87 /* EventKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FB975DF2ED7F9AA00E64A87 /* EventKit.framework */; }; 2FB975E02ED7F9AA00E64A87 /* EventKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FB975DF2ED7F9AA00E64A87 /* EventKit.framework */; };
2FBC22B52EF93B8A00882B6F /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FBC22B42EF93B8A00882B6F /* UniformTypeIdentifiers.framework */; };
2FBC22B72EF93BB400882B6F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FBC22B62EF93BB400882B6F /* UIKit.framework */; };
2FD1557B2ECAC5CC00233981 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FD1557A2ECAC5CC00233981 /* WebKit.framework */; }; 2FD1557B2ECAC5CC00233981 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FD1557A2ECAC5CC00233981 /* WebKit.framework */; };
2FD9A1AE2EF0FA7100A7B53E /* Network.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FD9A0B72EF0E85B00A7B53E /* Network.framework */; }; 2FD9A1AE2EF0FA7100A7B53E /* Network.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FD9A0B72EF0E85B00A7B53E /* Network.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
...@@ -25,6 +26,8 @@ ...@@ -25,6 +26,8 @@
2FAEF09B2E8B7BD40086EDA3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 2FAEF09B2E8B7BD40086EDA3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
2FB292292EAB04F400D9F164 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/Library/Frameworks/AVFoundation.framework; sourceTree = DEVELOPER_DIR; }; 2FB292292EAB04F400D9F164 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/Library/Frameworks/AVFoundation.framework; sourceTree = DEVELOPER_DIR; };
2FB975DF2ED7F9AA00E64A87 /* EventKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = EventKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.1.sdk/System/Library/Frameworks/EventKit.framework; sourceTree = DEVELOPER_DIR; }; 2FB975DF2ED7F9AA00E64A87 /* EventKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = EventKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.1.sdk/System/Library/Frameworks/EventKit.framework; sourceTree = DEVELOPER_DIR; };
2FBC22B42EF93B8A00882B6F /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; };
2FBC22B62EF93BB400882B6F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
2FD027B02EEFD25100CF5C33 /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.1.sdk/System/Library/Frameworks/Network.framework; sourceTree = DEVELOPER_DIR; }; 2FD027B02EEFD25100CF5C33 /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.1.sdk/System/Library/Frameworks/Network.framework; sourceTree = DEVELOPER_DIR; };
2FD1557A2ECAC5CC00233981 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.1.sdk/System/iOSSupport/System/Library/PrivateFrameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; }; 2FD1557A2ECAC5CC00233981 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.1.sdk/System/iOSSupport/System/Library/PrivateFrameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; };
2FD9A0B72EF0E85B00A7B53E /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = System/Library/Frameworks/Network.framework; sourceTree = SDKROOT; }; 2FD9A0B72EF0E85B00A7B53E /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = System/Library/Frameworks/Network.framework; sourceTree = SDKROOT; };
...@@ -34,7 +37,7 @@ ...@@ -34,7 +37,7 @@
2FD9A0B92EF0F7FD00A7B53E /* Exceptions for "QmsPluginFramework" folder in "QmsPluginFramework" target */ = { 2FD9A0B92EF0F7FD00A7B53E /* Exceptions for "QmsPluginFramework" folder in "QmsPluginFramework" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet; isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = ( membershipExceptions = (
FSCalendar/Info.plist, Utilities/FSCalendar/Info.plist,
); );
publicHeaders = ( publicHeaders = (
QmsPlugin.h, QmsPlugin.h,
...@@ -60,6 +63,8 @@ ...@@ -60,6 +63,8 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
2FBC22B72EF93BB400882B6F /* UIKit.framework in Frameworks */,
2FBC22B52EF93B8A00882B6F /* UniformTypeIdentifiers.framework in Frameworks */,
2FD9A1AE2EF0FA7100A7B53E /* Network.framework in Frameworks */, 2FD9A1AE2EF0FA7100A7B53E /* Network.framework in Frameworks */,
2FB975E02ED7F9AA00E64A87 /* EventKit.framework in Frameworks */, 2FB975E02ED7F9AA00E64A87 /* EventKit.framework in Frameworks */,
2FD1557B2ECAC5CC00233981 /* WebKit.framework in Frameworks */, 2FD1557B2ECAC5CC00233981 /* WebKit.framework in Frameworks */,
...@@ -67,7 +72,6 @@ ...@@ -67,7 +72,6 @@
2F99420D2EA9C5F000622199 /* QuartzCore.framework in Frameworks */, 2F99420D2EA9C5F000622199 /* QuartzCore.framework in Frameworks */,
2F8D09092E9605E200C764FF /* CoreGraphics.framework in Frameworks */, 2F8D09092E9605E200C764FF /* CoreGraphics.framework in Frameworks */,
2FAEF09C2E8B7BD40086EDA3 /* Foundation.framework in Frameworks */, 2FAEF09C2E8B7BD40086EDA3 /* Foundation.framework in Frameworks */,
2FAEF09A2E8B7BC80086EDA3 /* UIKit.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -94,6 +98,8 @@ ...@@ -94,6 +98,8 @@
2FAEF0982E8B7BC80086EDA3 /* Frameworks */ = { 2FAEF0982E8B7BC80086EDA3 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
2FBC22B62EF93BB400882B6F /* UIKit.framework */,
2FBC22B42EF93B8A00882B6F /* UniformTypeIdentifiers.framework */,
2FD027B02EEFD25100CF5C33 /* Network.framework */, 2FD027B02EEFD25100CF5C33 /* Network.framework */,
2FD9A0B72EF0E85B00A7B53E /* Network.framework */, 2FD9A0B72EF0E85B00A7B53E /* Network.framework */,
2FB975DF2ED7F9AA00E64A87 /* EventKit.framework */, 2FB975DF2ED7F9AA00E64A87 /* EventKit.framework */,
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "AccessAppointmentViewController.h" #import "AccessAppointmentViewController.h"
#import "AccessTransactionViewController.h" #import "AccessTransactionViewController.h"
#import "PullToRefresh.h"
// Custom Card // Custom Card
@interface SubmittedItemCell : UITableViewCell @interface SubmittedItemCell : UITableViewCell
...@@ -357,6 +358,11 @@ ...@@ -357,6 +358,11 @@
[self.tableView registerClass:[SubmittedItemCell class] forCellReuseIdentifier:@"SubmittedItemCell"]; [self.tableView registerClass:[SubmittedItemCell class] forCellReuseIdentifier:@"SubmittedItemCell"];
[self.tableView registerClass:[TransactionItemCell class] forCellReuseIdentifier:@"TransactionItemCell"]; [self.tableView registerClass:[TransactionItemCell class] forCellReuseIdentifier:@"TransactionItemCell"];
__weak typeof(self) weakSelf = self;
[self.tableView addPullToRefresh:^(UIRefreshControl *refresh) {
[weakSelf fetchAccessItem];
}];
[self.view addSubview:self.tableView]; [self.view addSubview:self.tableView];
...@@ -391,6 +397,7 @@ ...@@ -391,6 +397,7 @@
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData]; [self.tableView reloadData];
[self.tableView endPullToRefresh];
}); });
}]; }];
} }
......
// AddInfoViewController.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface AddInfoViewController : UIViewController
@property (nonatomic, strong) NSString *issueID;
@property (nonatomic, strong) NSString *planID;
@property (nonatomic, strong) NSString *status;
@property (nonatomic, strong) NSString *unitName;
@property (nonatomic, strong) NSString *reference;
@end
NS_ASSUME_NONNULL_END
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
#import "APIClient.h" #import "APIClient.h"
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "OfflineSyncManager.h"
#import "NetworkManager.h"
@interface AddIssueDetailsViewController () <UIImagePickerControllerDelegate, UINavigationControllerDelegate> @interface AddIssueDetailsViewController () <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
@property (nonatomic, strong) UIView *headerView; @property (nonatomic, strong) UIView *headerView;
...@@ -20,6 +22,7 @@ ...@@ -20,6 +22,7 @@
@property (nonatomic, assign) BOOL isEditMode; @property (nonatomic, assign) BOOL isEditMode;
@property (nonatomic, strong) NSMutableArray *existingImages; @property (nonatomic, strong) NSMutableArray *existingImages;
@property (nonatomic, strong) NSMutableArray *addedImages; @property (nonatomic, strong) NSMutableArray *addedImages;
@end @end
@implementation AddIssueDetailsViewController @implementation AddIssueDetailsViewController
...@@ -911,6 +914,20 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> * ...@@ -911,6 +914,20 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
}; };
NSLog(@"📦 Sending UpdateIssue with data: %@", updateData); NSLog(@"📦 Sending UpdateIssue with data: %@", updateData);
if (![NetworkManager.sharedManager isConnected]) {
[[OfflineSyncManager shared] queueUpdateIssue:updateData
images:self.addedImages];
[self showAlertWithTitle:@"Saved Offline"
message:@"This will be synced when you're back online."];
UIViewController *presenter = self.presentingViewController;
if (presenter.presentingViewController) {
[presenter.presentingViewController dismissViewControllerAnimated:YES completion:nil];
} else {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
[APIClient submitUpdateIssue:updateData [APIClient submitUpdateIssue:updateData
images:self.addedImages images:self.addedImages
...@@ -970,7 +987,23 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> * ...@@ -970,7 +987,23 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
@"comment": self.commentField.text ?: @"", @"comment": self.commentField.text ?: @"",
@"os": @"AND" @"os": @"AND"
}; };
NSLog(@"NetworkManager.sharedManager isConnected = %@",
[NetworkManager.sharedManager isConnected] ? @"YES" : @"NO");
if (![NetworkManager.sharedManager isConnected]) {
[[OfflineSyncManager shared] queueAddIssue:issueData
images:self.uploadedImages];
[self showAlertWithTitle:@"Saved Offline"
message:@"This will be synced when you're back online."];
UIViewController *presenter = self.presentingViewController;
if (presenter.presentingViewController) {
[presenter.presentingViewController dismissViewControllerAnimated:YES completion:nil];
} else {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
[APIClient submitAddIssue:issueData [APIClient submitAddIssue:issueData
images:self.uploadedImages images:self.uploadedImages
completion:^(NSDictionary *response, NSError *error) { completion:^(NSDictionary *response, NSError *error) {
...@@ -1014,7 +1047,6 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> * ...@@ -1014,7 +1047,6 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
} }
} }
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message { - (void)showAlertWithTitle:(NSString *)title message:(NSString *)message {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title UIAlertController *alert = [UIAlertController alertControllerWithTitle:title
message:message message:message
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#import "APIClient.h" #import "APIClient.h"
#import "CalendarView.h" #import "CalendarView.h"
#import "NSDate+CalendarHelpers.h" #import "NSDate+CalendarHelpers.h"
#import "AddInfoViewController.h"
@interface HistoryViewController () <UITableViewDelegate, UITableViewDataSource> @interface HistoryViewController () <UITableViewDelegate, UITableViewDataSource>
...@@ -78,7 +79,12 @@ ...@@ -78,7 +79,12 @@
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO; self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.titleLabel]; [self.headerView addSubview:self.titleLabel];
// need to make addInfo eventually self.addInfoButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.addInfoButton setImage:[UIImage systemImageNamed:@"document.badge.plus"] forState:UIControlStateNormal];
self.addInfoButton.tintColor = UIColor.whiteColor;
self.addInfoButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.addInfoButton addTarget:self action:@selector(addInfoTapped) forControlEvents:UIControlEventTouchUpInside];
[self.headerView addSubview:self.addInfoButton];
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[self.headerView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor], [self.headerView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
...@@ -88,6 +94,9 @@ ...@@ -88,6 +94,9 @@
[self.backButton.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor constant:12], [self.backButton.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor constant:12],
[self.backButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor], [self.backButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.addInfoButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-12],
[self.addInfoButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor], [self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor], [self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
...@@ -434,6 +443,14 @@ ...@@ -434,6 +443,14 @@
- (void)addInfoTapped { - (void)addInfoTapped {
NSLog(@"➕ Add Info tapped"); NSLog(@"➕ Add Info tapped");
AddInfoViewController *vc = [[AddInfoViewController alloc] init];
vc.issueID = self.issueID;
vc.planID = self.planID;
vc.status = self.status;
vc.unitName = self.unitName;
vc.reference = self.reference;
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
} }
#pragma mark - helper #pragma mark - helper
......
...@@ -170,7 +170,7 @@ ...@@ -170,7 +170,7 @@
// Title // Title
self.titleLabel = [[UILabel alloc] init]; self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"Issues"; self.titleLabel.text = @"Issue(s)";
self.titleLabel.textColor = UIColor.whiteColor; self.titleLabel.textColor = UIColor.whiteColor;
self.titleLabel.font = [UIFont boldSystemFontOfSize:20]; self.titleLabel.font = [UIFont boldSystemFontOfSize:20];
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO; self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
......
...@@ -44,10 +44,17 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -44,10 +44,17 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)fetchHistory:(NSString *)issueID + (void)fetchHistory:(NSString *)issueID
completion:(void (^)(NSDictionary *data, NSError *error))completion; completion:(void (^)(NSDictionary *data, NSError *error))completion;
+(void)fetchAddInfo:(NSString *)issueID +(void)submitAddInfo:(NSString *)issueID
remarks:(NSString *)remarks remarks:(NSString *)remarks
completion:(void (^)(NSDictionary *data, NSError *error))completion; completion:(void (^)(NSDictionary *data, NSError *error))completion;
+ (void)uploadAddInfo:(NSString *)issueID
remarks:(NSString *)remarks
images:(NSArray<UIImage *> *)images
mediaURL:(NSURL *)mediaURL
mediaType:(NSString *)mediaType
completion:(void (^)(NSDictionary *data, NSError *error))completion;
+ (void)fetchIssues:(void (^)(NSDictionary *data, NSError *error))completion; + (void)fetchIssues:(void (^)(NSDictionary *data, NSError *error))completion;
+ (void)fetchVoidIssue:(id)issueID + (void)fetchVoidIssue:(id)issueID
......
...@@ -390,10 +390,6 @@ ...@@ -390,10 +390,6 @@
appendFormField(@"data[comment]", updateData[@"comment"]); appendFormField(@"data[comment]", updateData[@"comment"]);
appendFormField(@"data[os]", updateData[@"os"]); appendFormField(@"data[os]", updateData[@"os"]);
// 🔹 Add `information` field (as JSON string)
// NSString *deviceInfo = @"{\"OS\":\"AND\",\"IMEI\":\"AND:2a2f13ff17b1cbb5\",\"OS_VERSION\":\"35\",\"MODEL\":\"2306EPN60G\",\"APP_VERSION\":\"1.22.26\"}";
// appendFormField(@"information", deviceInfo);
// 🔹 Append image files // 🔹 Append image files
for (int i = 0; i < images.count; i++) { for (int i = 0; i < images.count; i++) {
id maybeImage = images[i]; id maybeImage = images[i];
...@@ -454,7 +450,7 @@ ...@@ -454,7 +450,7 @@
}]; }];
[task resume]; [task resume];
} }
+ (void)fetchAppointment:(void (^)(NSDictionary *data, NSError *error))completion { + (void)fetchAppointment:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/appointment/getSlot?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSString *urlString = @"https://kitadev.commudesk.com/api/owner/appointment/getSlot?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
...@@ -984,7 +980,7 @@ ...@@ -984,7 +980,7 @@
[task resume]; [task resume];
} }
+(void)fetchAddInfo:(NSString *)issueID +(void)submitAddInfo:(NSString *)issueID
remarks:(NSString *)remarks remarks:(NSString *)remarks
completion:(void (^)(NSDictionary *data, NSError *error))completion { completion:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
...@@ -1061,6 +1057,124 @@ ...@@ -1061,6 +1057,124 @@
[task resume]; [task resume];
} }
+ (void)uploadAddInfo:(NSString *)issueID
remarks:(NSString *)remarks
images:(NSArray<UIImage *> *)images
mediaURL:(NSURL *)mediaURL
mediaType:(NSString *)mediaType
completion:(void (^)(NSDictionary *data, NSError *error))completion {
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/addInfo?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
// 🧱 Multipart body
NSString *boundary = [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]];
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]
forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
NSMutableData *body = [NSMutableData data];
// Helper for adding form fields
void (^appendFormField)(NSString *, NSString *) = ^(NSString *key, NSString *value) {
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key]
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"%@\r\n", value]
dataUsingEncoding:NSUTF8StringEncoding]];
};
// 🔹 Append OS info
appendFormField(@"data[issue_id]", issueID ?: @"");
appendFormField(@"data[remarks]", remarks ?: @"");
appendFormField(@"data[os]", @"AND");
// 🔹 Append images
if (images.count > 0) {
for (int i = 0; i < images.count; i++) {
id maybeImage = images[i];
if (![maybeImage isKindOfClass:[UIImage class]]) {
NSLog(@"⚠️ Skipping non-UIImage at index %d: %@", i, maybeImage);
continue; // skip invalid entries
}
UIImage *image = (UIImage *)maybeImage;
NSData *imageData = UIImageJPEGRepresentation(image, 0.8);
if (!imageData) continue;
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:
@"Content-Disposition: form-data; name=\"data[image][%d]\"; filename=\"image%d.jpg\"\r\n", i, i]
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:imageData];
[body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
}
// 🔹 Append media file if provided
if (mediaURL) {
NSData *fileData = [NSData dataWithContentsOfURL:mediaURL];
if (fileData) {
NSString *ext = mediaURL.pathExtension.lowercaseString;
NSString *mimeType = @"application/octet-stream";
if ([mediaType isEqualToString:@"Video"]) {
mimeType = [NSString stringWithFormat:@"video/%@", ext];
}
NSInteger index = images.count;
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:
@"Content-Disposition: form-data; name=\"data[image][%ld]\"; filename=\"addinfo.%@\"\r\n",
(long)index, ext]
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:
@"Content-Type: %@\r\n\r\n", mimeType]
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:fileData];
[body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
}
[body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
request.HTTPBody = body;
// Debug
NSLog(@"📦 [AddIssue] Uploading %lu image(s)", (unsigned long)images.count);
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"❌ [AddIssue] Network error: %@", error);
dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(nil, error); });
return;
}
NSError *jsonErr;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonErr];
if (jsonErr) {
NSString *raw = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"⚠️ [AddIssue] JSON parse error: %@\nRaw response: %@", jsonErr, raw);
dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(nil, jsonErr); });
return;
}
NSLog(@"✅ [AddIssue] Response: %@", json);
dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(json, nil); });
}];
[task resume];
}
+ (void)fetchIssues:(void (^)(NSDictionary *data, NSError *error))completion { + (void)fetchIssues:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/getIssue?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/getIssue?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
......
...@@ -45,17 +45,25 @@ ...@@ -45,17 +45,25 @@
nw_path_monitor_set_queue(self.monitor, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)); nw_path_monitor_set_queue(self.monitor, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
nw_path_monitor_set_update_handler(self.monitor, ^(nw_path_t path) { nw_path_monitor_set_update_handler(self.monitor, ^(nw_path_t path) {
self.connected = nw_path_get_status(path) == nw_path_status_satisfied;
NSLog(@"[NetworkManager] Internet connectivity: %@", self.connected ? @"YES" : @"NO");
// Post notification for all listeners BOOL hasInternet = (nw_path_get_status(path) == nw_path_status_satisfied);
// BOOL isWiFi = nw_path_uses_interface_type(path, nw_interface_type_wifi);
// BOOL isCellular = nw_path_uses_interface_type(path, nw_interface_type_cellular);
// BOOL isEthernet = nw_path_uses_interface_type(path, nw_interface_type_wired);
self.connected = hasInternet;
// && (isWiFi || isCellular || isEthernet);
// note that simulator might not report correct status
NSLog(@"[NetworkManager] Connected = %@", self.connected ? @"YES" : @"NO");
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkStatusChanged" [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkStatusChanged"
object:nil object:nil
userInfo:@{@"connected": @(self.connected)}]; userInfo:@{@"connected": @(self.connected)}];
}); });
}); });
nw_path_monitor_start(self.monitor); nw_path_monitor_start(self.monitor);
} }
......
// OfflineSyncManager.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface OfflineSyncManager : NSObject
+ (instancetype)shared;
// Queue actions
- (void)queueAddIssue:(NSDictionary *)payload
images:(NSArray<UIImage *> *)images;
- (void)queueUpdateIssue:(NSDictionary *)payload
images:(NSArray<UIImage *> *)images;
- (void)queueAddInfo:(NSString *)issueID
remarks:(NSString *)remarks
images:(NSArray<UIImage *> *)images
mediaURL:(NSURL * _Nullable)mediaURL
mediaType:(NSString * _Nullable)mediaType;
// Sync
- (void)processQueueIfOnline;
// Helpers
- (BOOL)hasPendingItems;
@end
NS_ASSUME_NONNULL_END
// OfflineSyncManager.mm
#import "OfflineSyncManager.h"
#import "LocalStorage.h"
#import "NetworkManager.h"
#import "APIClient.h"
static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE";
@implementation OfflineSyncManager
#pragma mark - Singleton
+ (instancetype)shared {
static OfflineSyncManager *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[OfflineSyncManager alloc] init];
[instance setupNetworkListener];
});
return instance;
}
#pragma mark - Setup
- (void)setupNetworkListener {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(networkStatusChanged:)
name:@"NetworkStatusChanged"
object:nil];
}
- (void)networkStatusChanged:(NSNotification *)note {
BOOL connected = [note.userInfo[@"connected"] boolValue];
if (connected) {
[self processQueueIfOnline];
}
}
#pragma mark - Queue Helpers (CRITICAL)
- (NSMutableArray *)safeLoadQueue {
id stored = [LocalStorage loadArrayForKey:kOfflineQueueKey];
if (![stored isKindOfClass:[NSArray class]]) {
NSLog(@"🧹 [OfflineSync] Queue corrupted — resetting");
[LocalStorage removeObjectForKey:kOfflineQueueKey];
return [NSMutableArray array];
}
NSMutableArray *validItems = [NSMutableArray array];
for (id obj in stored) {
if ([obj isKindOfClass:[NSDictionary class]]) {
[validItems addObject:obj];
}
}
return validItems;
}
- (void)saveQueue:(NSArray *)queue {
[LocalStorage saveArray:queue forKey:kOfflineQueueKey];
}
#pragma mark - Public Queue APIs
- (void)queueAddIssue:(NSDictionary *)payload
images:(NSArray<UIImage *> *)images {
[self enqueueAction:@"ADD_ISSUE"
payload:payload
images:images];
}
- (void)queueUpdateIssue:(NSDictionary *)payload
images:(NSArray<UIImage *> *)images {
[self enqueueAction:@"UPDATE_ISSUE"
payload:payload
images:images];
}
- (void)queueAddInfo:(NSString *)issueID
remarks:(NSString *)remarks
images:(NSArray<UIImage *> *)images
mediaURL:(NSURL * _Nullable)mediaURL
mediaType:(NSString * _Nullable)mediaType {
NSMutableArray *queue = [self safeLoadQueue];
NSArray *imagePaths = [self saveImagesToDisk:images];
NSString *mediaPath = [self saveMediaToDisk:mediaURL];
NSMutableDictionary *item = [NSMutableDictionary dictionary];
item[@"id"] = NSUUID.UUID.UUIDString;
item[@"type"] = @"ADD_INFO";
item[@"issue_id"] = issueID ?: @"";
item[@"remarks"] = remarks ?: @"";
item[@"imagePaths"] = imagePaths ?: @[];
item[@"timestamp"] = @((NSInteger)[NSDate date].timeIntervalSince1970);
if (mediaPath) item[@"mediaPath"] = mediaPath;
if (mediaType) item[@"mediaType"] = mediaType;
[queue addObject:item];
[self saveQueue:queue];
NSLog(@"📥 [OfflineSync] Queued ADD_INFO (%lu)", (unsigned long)queue.count);
}
#pragma mark - Core Queue Add
- (void)enqueueAction:(NSString *)type
payload:(NSDictionary *)payload
images:(NSArray<UIImage *> *)images {
NSMutableArray *queue = [self safeLoadQueue];
NSArray *imagePaths = [self saveImagesToDisk:images];
NSDictionary *item = @{
@"id": NSUUID.UUID.UUIDString,
@"type": type,
@"payload": payload ?: @{},
@"imagePaths": imagePaths ?: @[],
@"timestamp": @((NSInteger)[NSDate date].timeIntervalSince1970)
};
[queue addObject:item];
[self saveQueue:queue];
NSLog(@"📥 [OfflineSync] Queued %@ (%lu)", type, (unsigned long)queue.count);
}
#pragma mark - Processing Queue
- (void)processQueueIfOnline {
if (![NetworkManager.sharedManager isConnected]) {
return;
}
NSMutableArray *queue = [self safeLoadQueue];
if (queue.count == 0) return;
NSDictionary *item = queue.firstObject;
if (![item isKindOfClass:[NSDictionary class]]) {
[queue removeObjectAtIndex:0];
[self saveQueue:queue];
[self processQueueIfOnline];
return;
}
[self processItem:item completion:^(BOOL success) {
if (success) {
[queue removeObjectAtIndex:0];
[self saveQueue:queue];
[self processQueueIfOnline];
}
}];
}
- (void)processItem:(NSDictionary *)item
completion:(void (^)(BOOL success))completion {
NSString *type = item[@"type"] ?: @"";
if ([type isEqualToString:@"ADD_INFO"]) {
NSArray *images = [self loadImagesFromPaths:item[@"imagePaths"]];
NSURL *mediaURL = item[@"mediaPath"] ? [NSURL fileURLWithPath:item[@"mediaPath"]] : nil;
BOOL hasImages = images.count > 0;
BOOL hasMedia = mediaURL != nil;
if (!hasImages && !hasMedia) {
[APIClient submitAddInfo:item[@"issue_id"]
remarks:item[@"remarks"]
completion:^(NSDictionary *r, NSError *e) {
completion(e == nil);
}];
} else {
[APIClient uploadAddInfo:item[@"issue_id"]
remarks:item[@"remarks"]
images:images
mediaURL:mediaURL
mediaType:item[@"mediaType"]
completion:^(NSDictionary *r, NSError *e) {
completion(e == nil);
}];
}
return;
}
NSLog(@"⚠️ Unknown offline type: %@", type);
completion(NO);
}
#pragma mark - Disk Handling
- (NSArray *)saveImagesToDisk:(NSArray<UIImage *> *)images {
NSMutableArray *paths = [NSMutableArray array];
if (![images isKindOfClass:[NSArray class]]) return paths;
NSString *dir = [self offlineDirectory];
for (id obj in images) {
if (![obj isKindOfClass:[UIImage class]]) continue;
NSString *path = [dir stringByAppendingPathComponent:
[NSString stringWithFormat:@"%@.jpg", NSUUID.UUID.UUIDString]];
NSData *data = UIImageJPEGRepresentation(obj, 0.8);
if (data && [data writeToFile:path atomically:YES]) {
[paths addObject:path];
}
}
return paths;
}
- (NSString *)saveMediaToDisk:(NSURL *)url {
if (!url) return nil;
NSString *dir = [self offlineDirectory];
NSString *path = [dir stringByAppendingPathComponent:
[NSString stringWithFormat:@"%@.%@",
NSUUID.UUID.UUIDString, url.pathExtension]];
NSData *data = [NSData dataWithContentsOfURL:url];
if ([data writeToFile:path atomically:YES]) {
return path;
}
return nil;
}
- (NSArray *)loadImagesFromPaths:(NSArray *)paths {
NSMutableArray *images = [NSMutableArray array];
for (NSString *path in paths) {
UIImage *img = [UIImage imageWithContentsOfFile:path];
if (img) [images addObject:img];
}
return images;
}
- (NSString *)offlineDirectory {
NSString *dir = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
dir = [dir stringByAppendingPathComponent:@"offline_files"];
if (![[NSFileManager defaultManager] fileExistsAtPath:dir]) {
[[NSFileManager defaultManager] createDirectoryAtPath:dir
withIntermediateDirectories:YES
attributes:nil
error:nil];
}
return dir;
}
#pragma mark - Status
- (BOOL)hasPendingItems {
return ([self safeLoadQueue].count > 0);
}
@end
// PullToRefresh.h
#import <UIKit/UIKit.h>
typedef void (^PullToRefreshAction)(UIRefreshControl *refreshControl);
@interface UIScrollView (PullToRefresh)
- (UIRefreshControl *)addPullToRefresh:(PullToRefreshAction)action;
- (void)endPullToRefresh;
@end
// PullToRefresh.mm
#import "PullToRefresh.h"
#import <objc/runtime.h>
static char kRefreshControlKey;
static char kRefreshActionKey;
@implementation UIScrollView (PullToRefresh)
- (UIRefreshControl *)addPullToRefresh:(PullToRefreshAction)action {
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
objc_setAssociatedObject(self,
&kRefreshActionKey,
action,
OBJC_ASSOCIATION_COPY_NONATOMIC);
[refreshControl addTarget:self
action:@selector(handlePullToRefresh:)
forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;
objc_setAssociatedObject(self,
&kRefreshControlKey,
refreshControl,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return refreshControl;
}
- (void)handlePullToRefresh:(UIRefreshControl *)sender {
PullToRefreshAction action =
objc_getAssociatedObject(self, &kRefreshActionKey);
if (action) {
action(sender);
}
}
- (void)endPullToRefresh {
UIRefreshControl *refreshControl =
objc_getAssociatedObject(self, &kRefreshControlKey);
if (refreshControl.isRefreshing) {
[refreshControl endRefreshing];
}
}
@end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment