[📝] GIT init
This commit is contained in:
commit
0872089c5d
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
.Pods/
|
||||
Podfile
|
||||
Podfile.lock
|
||||
|
||||
JJUNGTABLE/Common/KEY.swift
|
1017
JJUNGTABLE.xcodeproj/project.pbxproj
Normal file
1017
JJUNGTABLE.xcodeproj/project.pbxproj
Normal file
File diff suppressed because it is too large
Load Diff
7
JJUNGTABLE.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
JJUNGTABLE.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,140 @@
|
|||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "abseil-cpp-binary",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/abseil-cpp-binary.git",
|
||||
"state" : {
|
||||
"revision" : "bfc0b6f81adc06ce5121eb23f628473638d67c5c",
|
||||
"version" : "1.2022062300.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "alamofire",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/Alamofire/Alamofire.git",
|
||||
"state" : {
|
||||
"revision" : "b2fa556e4e48cbf06cf8c63def138c98f4b811fa",
|
||||
"version" : "5.8.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "firebase-ios-sdk",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/firebase-ios-sdk",
|
||||
"state" : {
|
||||
"revision" : "837d4af6ead57cec1fc38007892500d3139c7556",
|
||||
"version" : "10.16.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googleappmeasurement",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleAppMeasurement.git",
|
||||
"state" : {
|
||||
"revision" : "56f681586ff006a7982b53dc94082eea31971acf",
|
||||
"version" : "10.16.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googledatatransport",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleDataTransport.git",
|
||||
"state" : {
|
||||
"revision" : "aae45a320fd0d11811820335b1eabc8753902a40",
|
||||
"version" : "9.2.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googleutilities",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleUtilities.git",
|
||||
"state" : {
|
||||
"revision" : "c38ce365d77b04a9a300c31061c5227589e5597b",
|
||||
"version" : "7.11.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "grpc-binary",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/grpc-binary.git",
|
||||
"state" : {
|
||||
"revision" : "a673bc2937fbe886dd1f99c401b01b6d977a9c98",
|
||||
"version" : "1.49.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "gtm-session-fetcher",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/gtm-session-fetcher.git",
|
||||
"state" : {
|
||||
"revision" : "d415594121c9e8a4f9d79cecee0965cf35e74dbd",
|
||||
"version" : "3.1.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "interop-ios-for-google-sdks",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/interop-ios-for-google-sdks.git",
|
||||
"state" : {
|
||||
"revision" : "2d12673670417654f08f5f90fdd62926dc3a2648",
|
||||
"version" : "100.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "kakao-ios-sdk",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kakao/kakao-ios-sdk",
|
||||
"state" : {
|
||||
"revision" : "4747117b01d8b28efbfcb004dbd122b8e1626f79",
|
||||
"version" : "2.18.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "leveldb",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/leveldb.git",
|
||||
"state" : {
|
||||
"revision" : "0706abcc6b0bd9cedfbb015ba840e4a780b5159b",
|
||||
"version" : "1.22.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "nanopb",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/nanopb.git",
|
||||
"state" : {
|
||||
"revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692",
|
||||
"version" : "2.30909.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "promises",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/promises.git",
|
||||
"state" : {
|
||||
"revision" : "e70e889c0196c76d22759eb50d6a0270ca9f1d9e",
|
||||
"version" : "2.3.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "snapkit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/SnapKit/SnapKit.git",
|
||||
"state" : {
|
||||
"revision" : "f222cbdf325885926566172f6f5f06af95473158",
|
||||
"version" : "5.6.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-protobuf",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-protobuf.git",
|
||||
"state" : {
|
||||
"revision" : "3c54ab05249f59f2c6641dd2920b8358ea9ed127",
|
||||
"version" : "1.24.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
BIN
JJUNGTABLE.xcodeproj/project.xcworkspace/xcuserdata/seankim.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
BIN
JJUNGTABLE.xcodeproj/project.xcworkspace/xcuserdata/seankim.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
Binary file not shown.
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1500"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "A7C240A72AE9F60D001E0925"
|
||||
BuildableName = "JJUNGTABLE.app"
|
||||
BlueprintName = "JJUNGTABLE"
|
||||
ReferencedContainer = "container:JJUNGTABLE.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "A7C240A72AE9F60D001E0925"
|
||||
BuildableName = "JJUNGTABLE.app"
|
||||
BlueprintName = "JJUNGTABLE"
|
||||
ReferencedContainer = "container:JJUNGTABLE.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "A7C240A72AE9F60D001E0925"
|
||||
BuildableName = "JJUNGTABLE.app"
|
||||
BlueprintName = "JJUNGTABLE"
|
||||
ReferencedContainer = "container:JJUNGTABLE.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
uuid = "E7C3BF9D-4509-4851-BCD9-5895856D2EBC"
|
||||
type = "1"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "8BB130DE-9616-4745-B407-9F71AE773D50"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "JJUNGTABLE/Scene/HomeScene/TopViewAlert/FriendView/FriendView.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "20"
|
||||
endingLineNumber = "20"
|
||||
landmarkName = "initData(_:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
|
@ -0,0 +1,85 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>JJUNGTABLE.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
<key>Promises (Playground) 1.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>5</integer>
|
||||
</dict>
|
||||
<key>Promises (Playground) 2.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>6</integer>
|
||||
</dict>
|
||||
<key>Promises (Playground).xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>SnapKitPlayground (Playground) 1.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
<key>SnapKitPlayground (Playground) 2.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
<key>SnapKitPlayground (Playground) 3.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>7</integer>
|
||||
</dict>
|
||||
<key>SnapKitPlayground (Playground) 4.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>8</integer>
|
||||
</dict>
|
||||
<key>SnapKitPlayground (Playground) 5.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>9</integer>
|
||||
</dict>
|
||||
<key>SnapKitPlayground (Playground).xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>A7C240A72AE9F60D001E0925</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
10
JJUNGTABLE.xcworkspace/contents.xcworkspacedata
generated
Normal file
10
JJUNGTABLE.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:JJUNGTABLE.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
149
JJUNGTABLE.xcworkspace/xcshareddata/swiftpm/Package.resolved
Normal file
149
JJUNGTABLE.xcworkspace/xcshareddata/swiftpm/Package.resolved
Normal file
|
@ -0,0 +1,149 @@
|
|||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "abseil-cpp-binary",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/abseil-cpp-binary.git",
|
||||
"state" : {
|
||||
"revision" : "bfc0b6f81adc06ce5121eb23f628473638d67c5c",
|
||||
"version" : "1.2022062300.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "alamofire",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/Alamofire/Alamofire.git",
|
||||
"state" : {
|
||||
"revision" : "3dc6a42c7727c49bf26508e29b0a0b35f9c7e1ad",
|
||||
"version" : "5.8.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "app-check",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/app-check.git",
|
||||
"state" : {
|
||||
"revision" : "5746b2d35c91c50581590ed97abe4c06b5037274",
|
||||
"version" : "10.18.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "firebase-ios-sdk",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/firebase-ios-sdk",
|
||||
"state" : {
|
||||
"revision" : "c60c958e707c50a9cf8bcb7cfd7d51c566d726c5",
|
||||
"version" : "10.19.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googleappmeasurement",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleAppMeasurement.git",
|
||||
"state" : {
|
||||
"revision" : "6b332152355c372ace9966d8ee76ed191f97025e",
|
||||
"version" : "10.17.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googledatatransport",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleDataTransport.git",
|
||||
"state" : {
|
||||
"revision" : "a732a4b47f59e4f725a2ea10f0c77e93a7131117",
|
||||
"version" : "9.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googleutilities",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleUtilities.git",
|
||||
"state" : {
|
||||
"revision" : "bc27fad73504f3d4af235de451f02ee22586ebd3",
|
||||
"version" : "7.12.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "grpc-binary",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/grpc-binary.git",
|
||||
"state" : {
|
||||
"revision" : "a673bc2937fbe886dd1f99c401b01b6d977a9c98",
|
||||
"version" : "1.49.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "gtm-session-fetcher",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/gtm-session-fetcher.git",
|
||||
"state" : {
|
||||
"revision" : "115f75e43851774934d695449a4836123c3246e1",
|
||||
"version" : "3.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "interop-ios-for-google-sdks",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/interop-ios-for-google-sdks.git",
|
||||
"state" : {
|
||||
"revision" : "2d12673670417654f08f5f90fdd62926dc3a2648",
|
||||
"version" : "100.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "kakao-ios-sdk",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kakao/kakao-ios-sdk",
|
||||
"state" : {
|
||||
"revision" : "ae3c60cbd4e3b348775f8c766e5b908fa1e66c5a",
|
||||
"version" : "2.20.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "leveldb",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/leveldb.git",
|
||||
"state" : {
|
||||
"revision" : "9d108e9112aa1d65ce508facf804674546116d9c",
|
||||
"version" : "1.22.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "nanopb",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/nanopb.git",
|
||||
"state" : {
|
||||
"revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692",
|
||||
"version" : "2.30909.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "promises",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/promises.git",
|
||||
"state" : {
|
||||
"revision" : "e70e889c0196c76d22759eb50d6a0270ca9f1d9e",
|
||||
"version" : "2.3.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "snapkit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/SnapKit/SnapKit.git",
|
||||
"state" : {
|
||||
"revision" : "f222cbdf325885926566172f6f5f06af95473158",
|
||||
"version" : "5.6.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-protobuf",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-protobuf.git",
|
||||
"state" : {
|
||||
"revision" : "65e8f29b2d63c4e38e736b25c27b83e012159be8",
|
||||
"version" : "1.25.2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
BIN
JJUNGTABLE.xcworkspace/xcuserdata/seankim.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
BIN
JJUNGTABLE.xcworkspace/xcuserdata/seankim.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
Binary file not shown.
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
uuid = "821E3B5D-51C3-4FFC-B087-93F99A77899C"
|
||||
type = "0"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "A46E5F1A-275E-4724-80DE-0B342715A93D"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "JJUNGTABLE/Scene/Common/IntroViewController.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "21"
|
||||
endingLineNumber = "21"
|
||||
landmarkName = "IntroViewController"
|
||||
landmarkType = "3">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "24F20DB5-D9D5-46D5-87A4-73D88CBC9C7B"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "JJUNGTABLE/Scene/Common/IntroViewController.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "79"
|
||||
endingLineNumber = "79"
|
||||
landmarkName = "gotoMainVC()"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
|
@ -0,0 +1,93 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>Promises (Playground) 1.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>5</integer>
|
||||
</dict>
|
||||
<key>Promises (Playground) 2.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>6</integer>
|
||||
</dict>
|
||||
<key>Promises (Playground) 3.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>12</integer>
|
||||
</dict>
|
||||
<key>Promises (Playground) 4.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>13</integer>
|
||||
</dict>
|
||||
<key>Promises (Playground) 5.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>14</integer>
|
||||
</dict>
|
||||
<key>Promises (Playground).xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
<key>SnapKitPlayground (Playground) 1.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>8</integer>
|
||||
</dict>
|
||||
<key>SnapKitPlayground (Playground) 2.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>9</integer>
|
||||
</dict>
|
||||
<key>SnapKitPlayground (Playground) 3.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
<key>SnapKitPlayground (Playground) 4.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>10</integer>
|
||||
</dict>
|
||||
<key>SnapKitPlayground (Playground) 5.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>11</integer>
|
||||
</dict>
|
||||
<key>SnapKitPlayground (Playground).xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>7</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
BIN
JJUNGTABLE/.DS_Store
vendored
Normal file
BIN
JJUNGTABLE/.DS_Store
vendored
Normal file
Binary file not shown.
376
JJUNGTABLE/APP/AppDelegate.swift
Normal file
376
JJUNGTABLE/APP/AppDelegate.swift
Normal file
|
@ -0,0 +1,376 @@
|
|||
//
|
||||
// AppDelegate.swift
|
||||
// JJUNGTABLE
|
||||
//
|
||||
// Created by Sean Kim on 10/26/23.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
import UserNotifications
|
||||
|
||||
import Firebase
|
||||
import FirebaseMessaging
|
||||
|
||||
import KakaoSDKCommon
|
||||
|
||||
|
||||
|
||||
|
||||
@main
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate{
|
||||
var viewModel = ViewModel()
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var version: [Version] = []
|
||||
|
||||
private let gcmMessageIDKey = "gcm.message_id"
|
||||
|
||||
// private var cancellables = Set<AnyCancellable>()
|
||||
// private var versions: [Version] = []
|
||||
|
||||
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
||||
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
|
||||
}
|
||||
|
||||
private func checkVersion() {
|
||||
API.shared.readData(path: "/db/version/read",
|
||||
queryItems: [URLQueryItem(name: "os_type", value: "I")])
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .finished:
|
||||
printLog(self.version)
|
||||
|
||||
//현재 버전이 업데이트 버전보다 OOO 하다
|
||||
switch compareVersion(currentVersion(), self.version[0].update_ver) {
|
||||
case .bigger:
|
||||
self.viewModel.isUpdate = "N"
|
||||
case .smaller, .equal:
|
||||
self.viewModel.isUpdate = "Y"
|
||||
case .error:
|
||||
self.viewModel.alertData = self.viewModel.systemErrorAlert()
|
||||
self.viewModel.showAlert.toggle()
|
||||
}
|
||||
|
||||
switch compareVersion(currentVersion(), self.version[0].final_ver) {
|
||||
case .bigger, .equal:
|
||||
// 현재 버전이 스토어 버전보다 높거나 같을 같을 떄 - 이상 x
|
||||
break
|
||||
case .smaller:
|
||||
let choice = self.version[0].update_choice == "Y" ? true : false
|
||||
switch compareVersion(currentVersion(), self.version[0].force_ver) {
|
||||
case .bigger, .equal:
|
||||
// 현재 버전이 강업보다 높다
|
||||
if !choice {
|
||||
break // 업데이트를 안내할 필요 없음
|
||||
}
|
||||
else {
|
||||
self.viewModel.alertData = .init(title: "업데이트 안내",
|
||||
body: """
|
||||
앱 최신 버전이 새로 나왔습니다.
|
||||
지금 업데이트 하시겠어요?
|
||||
""",
|
||||
button: [ButtonType(name: "지금 업데이트", role: .cancel ,
|
||||
function: {exit(1)}),
|
||||
ButtonType(name: "나중에", role: .none ,
|
||||
function: nil),
|
||||
])
|
||||
self.viewModel.showAlert.toggle()
|
||||
}
|
||||
case .smaller:
|
||||
// 현재 버전이 강업보다 닞다 -> 강업을 해야 한다.
|
||||
self.viewModel.alertData = .init(title: "업데이트 안내",
|
||||
body: """
|
||||
시스템이 제대로 동작하지 못했습니다.
|
||||
앱을 종료후 다시 실행해주세요.
|
||||
""",
|
||||
button: [ButtonType(name: "지금 업데이트", role: .none,
|
||||
function: {exit(1)})])
|
||||
self.viewModel.showAlert.toggle()
|
||||
case .error:
|
||||
self.viewModel.alertData = self.viewModel.systemErrorAlert()
|
||||
self.viewModel.showAlert.toggle()
|
||||
}
|
||||
case .error:
|
||||
self.viewModel.alertData = self.viewModel.systemErrorAlert()
|
||||
self.viewModel.showAlert.toggle()
|
||||
}
|
||||
// 버전 비교 처리
|
||||
case .failure(let error):
|
||||
printLog(error)
|
||||
self.viewModel.alertData = self.viewModel.systemErrorAlert()
|
||||
self.viewModel.showAlert.toggle()
|
||||
}
|
||||
} receiveValue: { data in
|
||||
do {
|
||||
let decoder = JSONDecoder()
|
||||
self.version = try decoder.decode([Version].self, from: data)
|
||||
} catch {
|
||||
printLog(error)
|
||||
self.viewModel.alertData = self.viewModel.systemErrorAlert()
|
||||
self.viewModel.showAlert.toggle()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - 기초 셋업
|
||||
extension AppDelegate {
|
||||
func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
||||
printLog("Start Set AppDelegate")
|
||||
if isIllegalDevice() {
|
||||
viewModel.alertData = .init(title:"경고",
|
||||
body: """
|
||||
변경된 OS('탈옥'등)의 스마트폰은 서비스를 이용할수 없습니다.
|
||||
""",
|
||||
button: [ButtonType(name: "확인", role: .none ,
|
||||
function: {exit(1)})])
|
||||
viewModel.showAlert.toggle()
|
||||
}
|
||||
else {
|
||||
checkVersion()
|
||||
|
||||
// Firebase 설정
|
||||
FirebaseApp.configure()
|
||||
|
||||
|
||||
// App Check 설정
|
||||
// AppCheck.setAppCheckProviderFactory(AppCheckDebugProviderFactory())
|
||||
|
||||
// 카카오 SDK 초기화 코드
|
||||
KakaoSDK.initSDK(appKey: KEY.kakaoAppKey)
|
||||
|
||||
#if FCM
|
||||
// FCM 설정
|
||||
Messaging.messaging().delegate = self
|
||||
#endif
|
||||
// Noti 설정
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
|
||||
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { granted, error in
|
||||
if let error = error {
|
||||
printLog("Authorization error: \(error.localizedDescription)")
|
||||
}
|
||||
if granted {
|
||||
DispatchQueue.main.async {
|
||||
UIApplication.shared.registerForRemoteNotifications()
|
||||
}
|
||||
} else {
|
||||
printLog("Notification permission denied.")
|
||||
}
|
||||
}
|
||||
|
||||
application.registerForRemoteNotifications()
|
||||
UIApplication.shared.registerForRemoteNotifications()
|
||||
|
||||
// 네트워크 모니터 초기화
|
||||
_ = NetworkMonitor.shared
|
||||
}
|
||||
|
||||
printLog("End Set AppDelegate")
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension AppDelegate {
|
||||
|
||||
// 24.05.30 (수정완) - 앱이 원격 푸시 알림 등록을 성공적으로 마친 후 호출
|
||||
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
||||
|
||||
// 파이어 베이스 SMS 하려고 하는 세팅
|
||||
Auth.auth().setAPNSToken(deviceToken, type: .unknown)
|
||||
|
||||
printLog("원격 푸시 알림 등록 성공 ")
|
||||
|
||||
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02.2hX", $1)})
|
||||
viewModel.deviceToken = deviceTokenString
|
||||
|
||||
// Firebase에 디바이스 토큰 등록
|
||||
Messaging.messaging().apnsToken = deviceToken
|
||||
printLog("DeviceToken: \(viewModel.deviceToken)")
|
||||
}
|
||||
|
||||
// 24.05.30 (수정완) - 초기 FCM토큰 등록
|
||||
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
|
||||
@UserDefault (key: "isRegisterToken", defaultValue: "F") var isRegisterToken
|
||||
printLog("토큰 등록 동작 시작")
|
||||
guard let fcmToken = fcmToken else { return }
|
||||
viewModel.pushFCMToken = fcmToken
|
||||
|
||||
Task {
|
||||
if isRegisterToken == "F" {
|
||||
// switch await FB_CRUD.shared.create("Token", doc: "\(viewModel.deviceToken)",
|
||||
// data: ["fcmToken":"\(viewModel.pushFCMToken)"]) {
|
||||
// case .success(_):
|
||||
// viewModel.pushFCMToken = fcmToken
|
||||
// isRegisterToken = "T"
|
||||
// printLog("초기 토큰 등록 성공")
|
||||
// case .failure(let error):
|
||||
// isRegisterToken = "F"
|
||||
// viewModel.alertData = .init(body:"""
|
||||
// 시스템이 제대로 동작하지 못했습니다.
|
||||
// 앱을 종료후 다시 실행해주세요.
|
||||
// """,
|
||||
// button: [ButtonType(name: "종료", role: .destructive ,function: {exit(1)})])
|
||||
// viewModel.showAlert.toggle()
|
||||
// printLog("초기 토큰 등록 실패")
|
||||
// }
|
||||
}
|
||||
else {
|
||||
// if viewModel.pushFCMToken != fcmToken {
|
||||
// switch await FB_CRUD.shared.update("Token", doc: "\(viewModel.deviceToken)",
|
||||
// data: ["fcmToken":"\(viewModel.pushFCMToken)"]) {
|
||||
// case .success(_):
|
||||
// viewModel.pushFCMToken = fcmToken
|
||||
// isRegisterToken = "T"
|
||||
// printLog("토큰 재등록 성공")
|
||||
// case .failure(_):
|
||||
// viewModel.alertData = .init(body:"""
|
||||
// 시스템이 제대로 동작하지 못했습니다.
|
||||
// 앱을 종료후 다시 실행해주세요.
|
||||
// """,
|
||||
// button: [ButtonType(name: "종료", role: .destructive ,function: {exit(1)})])
|
||||
// viewModel.showAlert.toggle()
|
||||
// printLog("토큰 재등록 실패")
|
||||
// }
|
||||
// }
|
||||
}
|
||||
printLog("FCM Token: \(viewModel.pushFCMToken)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 앱 켜져있을때 알럿 받으면
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
|
||||
|
||||
let userInfo = notification.request.content.userInfo
|
||||
|
||||
//파이어 베이스 SMS 수신설정 부
|
||||
if Auth.auth().canHandleNotification(userInfo) {
|
||||
return []
|
||||
}
|
||||
|
||||
if let apsData = userInfo["aps"] as? [AnyHashable: Any],
|
||||
let messageId = userInfo["gcm.message_id"] as? String,
|
||||
let badge = apsData["badge"] as? Int {
|
||||
|
||||
// switch await FB_CRUD.shared.read("Users", doc: "\(viewModel.userId)") {
|
||||
// case .success(let data):
|
||||
// if var noReadList = data["noReadAlert"] as? [String] {
|
||||
// noReadList.append(messageId)
|
||||
// viewModel.noReadAlertList = noReadList
|
||||
// switch await FB_CRUD.shared.update("Users", doc: "\(viewModel.userId)",
|
||||
// data: ["noReadAlert":noReadList]) {
|
||||
// case .success(let data):
|
||||
// await viewModel.setBadge()
|
||||
// case .failure(let gg):
|
||||
// printLog(noReadList)
|
||||
// }
|
||||
// printLog(viewModel.noReadAlertList)
|
||||
// printLog(viewModel.noReadAlertList.count)
|
||||
//
|
||||
// }
|
||||
// case .failure(let failure):
|
||||
// printLog(failure)
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
Messaging.messaging().appDidReceiveMessage(userInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
if #available(iOS 14.0, *) {
|
||||
return [[.list,.banner,.sound]]
|
||||
} else {
|
||||
return[[.alert,.sound]]
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Background 상태에서 수신 받은 Notification을 터치해 앱으로 진입했을 때 호출
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) {
|
||||
printLog("ININININININi")
|
||||
let userInfo = response.notification.request.content.userInfo
|
||||
|
||||
//파이어 베이스 SMS 수신설정 부
|
||||
if Auth.auth().canHandleNotification(userInfo) {
|
||||
return
|
||||
}
|
||||
|
||||
if let apsData = userInfo["aps"] as? [AnyHashable: Any] {
|
||||
if let alert = apsData["alert"] as? [AnyHashable: Any],
|
||||
let messageKey = apsData[gcmMessageIDKey] as? String {
|
||||
printLog("apsData: \(apsData)")
|
||||
printLog("alert: \(alert)")
|
||||
printLog("key: \(messageKey)")
|
||||
if let param = alert["parameter"] {
|
||||
printLog(param as? String)
|
||||
}
|
||||
// viewModel.setBadge()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
||||
|
||||
// 파이어 베이스 SMS 설정부
|
||||
if Auth.auth().canHandleNotification(userInfo) {
|
||||
completionHandler(.noData)
|
||||
return
|
||||
}
|
||||
|
||||
// printLog("Back = \(userInfo)")
|
||||
// FB_CRUD.shared.sendPushNotification("확인", body: "확인")
|
||||
// @UserDefault (key: "fcmToken", defaultValue: "") var pushFCMToken
|
||||
// @UserDefault (key: "userId", defaultValue: "") var userId
|
||||
// @UserDefault (key: "noReadAlertList", defaultValue: [""]) var noReadAlertList
|
||||
//
|
||||
// let userInfo = notification.request.content.userInfo
|
||||
//
|
||||
// if let apsData = userInfo["aps"] as? [AnyHashable: Any],
|
||||
// let messageId = userInfo["gcm.message_id"] as? String,
|
||||
// let badge = apsData["badge"] as? Int {
|
||||
//
|
||||
// switch await FB_CRUD.shared.read("Users", doc: "\(userId)") {
|
||||
// case .success(let data):
|
||||
// printLog(data)
|
||||
// if var noReadList = data["noReadAlert"] as? [String] {
|
||||
// noReadList.append(messageId)
|
||||
// noReadAlertList = noReadList
|
||||
// await FB_CRUD.shared.update("Users", doc: "\(userId)",
|
||||
// data: ["noReadAlert":noReadList])
|
||||
// printLog(noReadList)
|
||||
// printLog(noReadAlertList)
|
||||
// printLog(noReadAlertList.count)
|
||||
// await viewModel.setBadge(badge: noReadAlertList.count)
|
||||
// }
|
||||
// case .failure(let failure):
|
||||
// printLog(failure)
|
||||
// }
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
88
JJUNGTABLE/APP/SceneDelegate.swift
Normal file
88
JJUNGTABLE/APP/SceneDelegate.swift
Normal file
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// SceneDelegate.swift
|
||||
// JJUNGTABLE
|
||||
//
|
||||
// Created by Sean Kim on 10/26/23.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
|
||||
import KakaoSDKAuth
|
||||
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
// private var appCoordinator: AppCordinator?
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
|
||||
guard let windowScene = scene as? UIWindowScene else { return }
|
||||
var contentView = ContentView().environmentObject(appDelegate.viewModel)
|
||||
|
||||
window = UIWindow(windowScene: windowScene)
|
||||
window?.rootViewController = UIHostingController(rootView: contentView)
|
||||
window?.makeKeyAndVisible()
|
||||
// self.window = window
|
||||
|
||||
/*
|
||||
let navigationController = UINavigationController()
|
||||
|
||||
navigationController.navigationBar.isHidden = true
|
||||
navigationController.interactivePopGestureRecognizer?.isEnabled = false
|
||||
|
||||
self.appCoordinator = AppCordinator(navigationController: navigationController)
|
||||
self.appCoordinator?.choiceVC(.intro, isPop: F)
|
||||
|
||||
window = UIWindow(windowScene: windowScene)
|
||||
window?.windowScene = windowScene
|
||||
window?.rootViewController = navigationController
|
||||
window?.makeKeyAndVisible()
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
func sceneDidDisconnect(_ scene: UIScene) {
|
||||
// Called as the scene is being released by the system.
|
||||
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
||||
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
||||
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
|
||||
}
|
||||
|
||||
func sceneDidBecomeActive(_ scene: UIScene) {
|
||||
// Called when the scene has moved from an inactive state to an active state.
|
||||
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
||||
}
|
||||
|
||||
func sceneWillResignActive(_ scene: UIScene) {
|
||||
// Called when the scene will move from an active state to an inactive state.
|
||||
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
||||
}
|
||||
|
||||
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||
// Called as the scene transitions from the background to the foreground.
|
||||
// Use this method to undo the changes made on entering the background.
|
||||
}
|
||||
|
||||
func sceneDidEnterBackground(_ scene: UIScene) {
|
||||
// Called as the scene transitions from the foreground to the background.
|
||||
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
||||
// to restore the scene back to its current state.
|
||||
}
|
||||
|
||||
|
||||
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
|
||||
|
||||
// 카카오톡 설정 부분
|
||||
if let url = URLContexts.first?.url {
|
||||
if url.scheme == "특정스키마" {
|
||||
//동작
|
||||
}
|
||||
else if AuthApi.isKakaoTalkLoginUrl(url) {
|
||||
_ = AuthController.handleOpenUrl(url: url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
BIN
JJUNGTABLE/Assets.xcassets/.DS_Store
vendored
Normal file
BIN
JJUNGTABLE/Assets.xcassets/.DS_Store
vendored
Normal file
Binary file not shown.
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "JJungTable_Icon.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 543 KiB |
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "68",
|
||||
"green" : "83",
|
||||
"red" : "230"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.267",
|
||||
"green" : "0.325",
|
||||
"red" : "0.902"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"localizable" : true
|
||||
}
|
||||
}
|
6
JJUNGTABLE/Assets.xcassets/1. New Set/Contents.json
Normal file
6
JJUNGTABLE/Assets.xcassets/1. New Set/Contents.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.294",
|
||||
"green" : "0.886",
|
||||
"red" : "0.965"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "184",
|
||||
"green" : "220",
|
||||
"red" : "255"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
6
JJUNGTABLE/Assets.xcassets/Contents.json
Normal file
6
JJUNGTABLE/Assets.xcassets/Contents.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.310",
|
||||
"green" : "0.800",
|
||||
"red" : "0.969"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.699",
|
||||
"green" : "0.699",
|
||||
"red" : "0.699"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.699",
|
||||
"green" : "0.699",
|
||||
"red" : "0.699"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0",
|
||||
"green" : "102",
|
||||
"red" : "255"
|
||||
}
|
||||
},
|
||||
"idiom" : "iphone"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"localizable" : true
|
||||
}
|
||||
}
|
6
JJUNGTABLE/Assets.xcassets/Old Color/Contents.json
Normal file
6
JJUNGTABLE/Assets.xcassets/Old Color/Contents.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.250",
|
||||
"green" : "0.650",
|
||||
"red" : "0.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.251",
|
||||
"green" : "0.251",
|
||||
"red" : "0.251"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.251",
|
||||
"green" : "0.251",
|
||||
"red" : "0.251"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "0.200",
|
||||
"blue" : "0.698",
|
||||
"green" : "0.698",
|
||||
"red" : "0.698"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "0.200",
|
||||
"blue" : "0.698",
|
||||
"green" : "0.698",
|
||||
"red" : "0.698"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
68
JJUNGTABLE/Base.lproj/LaunchScreen.storyboard
Normal file
68
JJUNGTABLE/Base.lproj/LaunchScreen.storyboard
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
|
||||
<capability name="Image references" minToolsVersion="12.0"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<customFonts key="customFonts">
|
||||
<array key="NPSfont_regular.otf">
|
||||
<string>NPS-font-Regular</string>
|
||||
</array>
|
||||
</customFonts>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="gTx-ol-ybK">
|
||||
<rect key="frame" x="114.66666666666669" y="363.33333333333337" width="164" height="152.33333333333337"/>
|
||||
<imageReference key="image" image="j.square.on.square" catalog="system" symbolScale="large" renderingMode="hierarchical">
|
||||
<hierarchicalColors>
|
||||
<color name="BrandColor"/>
|
||||
<color name="BrandColor"/>
|
||||
<color name="BrandColor"/>
|
||||
</hierarchicalColors>
|
||||
</imageReference>
|
||||
<preferredSymbolConfiguration key="preferredSymbolConfiguration" configurationType="pointSize" pointSize="100" scale="large"/>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="© 2023. Sean.Kim" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jwU-9l-Qnk">
|
||||
<rect key="frame" x="144.33333333333334" y="790.33333333333337" width="104.66666666666666" height="17.666666666666629"/>
|
||||
<fontDescription key="fontDescription" name="NPS-font-Regular" family="NPS font" pointSize="12"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="jwU-9l-Qnk" secondAttribute="bottom" constant="10" id="J8u-6m-q7i"/>
|
||||
<constraint firstItem="jwU-9l-Qnk" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="Ukm-3B-467"/>
|
||||
<constraint firstItem="gTx-ol-ybK" firstAttribute="centerY" secondItem="6Tk-OE-BBY" secondAttribute="centerY" id="enm-4c-d2c"/>
|
||||
<constraint firstItem="gTx-ol-ybK" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="zE7-OK-N1V"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="52.671755725190835" y="374.64788732394368"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="j.square.on.square" catalog="system" width="128" height="116"/>
|
||||
<namedColor name="BrandColor">
|
||||
<color red="0.90196079019999997" green="0.32549020649999999" blue="0.26666668059999998" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
|
||||
</namedColor>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
93
JJUNGTABLE/Common/API/API.swift
Normal file
93
JJUNGTABLE/Common/API/API.swift
Normal file
|
@ -0,0 +1,93 @@
|
|||
//
|
||||
// API.swift
|
||||
// JJUNGTABLE
|
||||
//
|
||||
// Created by Sean Kim on 6/5/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
struct InfoKey {
|
||||
static var httpURL: String {
|
||||
return Bundle.main.object(forInfoDictionaryKey: "httpURL") as? String ?? ""
|
||||
}
|
||||
|
||||
static var httpsURL: String {
|
||||
return Bundle.main.object(forInfoDictionaryKey: "httpsURL") as? String ?? ""
|
||||
}
|
||||
|
||||
static var apiPort: String {
|
||||
return Bundle.main.object(forInfoDictionaryKey: "PORT") as? String ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
class API {
|
||||
static let shared = API()
|
||||
|
||||
private init() {}
|
||||
|
||||
private func makeURLComponents(path: String, queryItems: [URLQueryItem]? = nil) -> Result<URLComponents, Error> {
|
||||
// let url = InfoKey.httpURL
|
||||
// let port = InfoKey.apiPort
|
||||
let url = "https://ipstein.myds.me"
|
||||
let port = "5004"
|
||||
let path = "/JJ\(path)"
|
||||
|
||||
printLog("\(url):\(port)\(path)?\(queryItems)")
|
||||
|
||||
if url == "" || port == "" {
|
||||
return .failure(API_ERROR(caseType: .API_PLIST_WRONG, message: "저장된 API 주소 오류"))
|
||||
}
|
||||
|
||||
guard var components = URLComponents(string: url) else {
|
||||
return .failure(API_ERROR(caseType: .API_PATH_WRONG, message: "API 주소 오류"))
|
||||
}
|
||||
components.port = Int(port)
|
||||
components.path = path
|
||||
components.queryItems = queryItems
|
||||
|
||||
return .success(components)
|
||||
}
|
||||
|
||||
func createUserInfo() -> AnyPublisher<[UserInfo], Error> {
|
||||
// switch self.makeURLComponents() {
|
||||
// case .success(let components):
|
||||
// break
|
||||
// case .failure(let error):
|
||||
// return Fail(error: error)
|
||||
// .eraseToAnyPublisher()
|
||||
// }
|
||||
|
||||
return Fail(error: API_ERROR(caseType: .API_PATH_WRONG, message: "API 주소 오류"))
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func readData(path: String, queryItems: [URLQueryItem]? = nil) -> AnyPublisher<Data, Error> {
|
||||
switch self.makeURLComponents(path: path, queryItems: queryItems) {
|
||||
case .success(let components):
|
||||
guard let url = components.url else {
|
||||
return Fail(error: API_ERROR(caseType: .API_PATH_WRONG, message: "API 주소 오류"))
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
return URLSession.shared.dataTaskPublisher(for: url)
|
||||
.tryMap { result in
|
||||
guard let httpResponse = result.response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
|
||||
throw API_ERROR(caseType: .API_CONNECT, message: "서버 접속 오류")
|
||||
}
|
||||
return result.data
|
||||
}
|
||||
.mapError { error in
|
||||
return API_ERROR(caseType: .API_READ, message: "\(error.localizedDescription)")
|
||||
}
|
||||
.receive(on: DispatchQueue.main)
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
case .failure(let error):
|
||||
return Fail(error: error)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
39
JJUNGTABLE/Common/API/DataModel.swift
Normal file
39
JJUNGTABLE/Common/API/DataModel.swift
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// DataModel.swift
|
||||
// JJUNGTABLE
|
||||
//
|
||||
// Created by Sean Kim on 6/10/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Version: Codable {
|
||||
let os_type: String
|
||||
let final_ver: String
|
||||
let force_ver: String
|
||||
let update_ver: String
|
||||
let update_choice: String
|
||||
}
|
||||
|
||||
struct UserInfo: Codable, Identifiable {
|
||||
let id: String
|
||||
let connect_type: String
|
||||
let os_type: String
|
||||
let table_id: String
|
||||
let name: String
|
||||
let birth: String
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case id = "user_cid"
|
||||
case connect_type
|
||||
case os_type
|
||||
case table_id
|
||||
case name
|
||||
case birth
|
||||
}
|
||||
}
|
||||
|
||||
struct UserCID: Codable {
|
||||
let user_cid: String
|
||||
}
|
||||
|
97
JJUNGTABLE/Common/CustomView/HalfView.swift
Normal file
97
JJUNGTABLE/Common/CustomView/HalfView.swift
Normal file
|
@ -0,0 +1,97 @@
|
|||
//
|
||||
// HalfView.swift
|
||||
// PersonalHealthDiary
|
||||
//
|
||||
// Created by Sean Kim on 3/6/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
//struct HalfView: View {
|
||||
// @State var showSheet: Bool = false
|
||||
//
|
||||
// var body: some View {
|
||||
//// NavigationView {
|
||||
// Button {
|
||||
// showSheet.toggle()
|
||||
// } label: {
|
||||
// Text("Button Click2")
|
||||
// }
|
||||
// .halfSheet(showSheet: $showSheet) {
|
||||
// Text("HELLO HALF")
|
||||
// } onEnd: {
|
||||
// print("?")
|
||||
// }
|
||||
// // }
|
||||
// }
|
||||
//}
|
||||
|
||||
extension View {
|
||||
func halfSheet<SheetView: View>(showSheet: Binding<Bool>, @ViewBuilder sheetView: @escaping ()->SheetView, onEnd: @escaping ()->() ) -> some View {
|
||||
return self
|
||||
.background(
|
||||
// 사용하는 이유로는 스유의 사이즈로만 사용할것이니까
|
||||
HalfSheetHelper(sheetView: sheetView(), showSheet: showSheet, onEnd: onEnd)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//UIKit을 써야 하는것 같음
|
||||
struct HalfSheetHelper<SheetView: View>: UIViewControllerRepresentable {
|
||||
|
||||
var sheetView: SheetView
|
||||
@Binding var showSheet: Bool
|
||||
var onEnd: ()->()
|
||||
// 나중에 호출시 이거 순서 따라서 인수 위치도 바뀜
|
||||
|
||||
let vc = UIViewController()
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
return Coordinator(parent: self)
|
||||
}
|
||||
|
||||
func makeUIViewController(context: Context) -> UIViewController {
|
||||
vc.view.backgroundColor = .clear
|
||||
return vc
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
|
||||
if showSheet {
|
||||
let sheetController = CustomHostingController(rootView: sheetView)
|
||||
sheetController.presentationController?.delegate = context.coordinator
|
||||
uiViewController.present(sheetController, animated: true)
|
||||
}
|
||||
else {
|
||||
// showSheet 가 토글 되어 view 가 닫힐 때
|
||||
// uiViewController.dismiss(animated: true)
|
||||
uiViewController.presentedViewController?.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
// 종료
|
||||
class Coordinator: NSObject, UISheetPresentationControllerDelegate {
|
||||
|
||||
var parent: HalfSheetHelper
|
||||
|
||||
init(parent: HalfSheetHelper) {
|
||||
self.parent = parent
|
||||
}
|
||||
|
||||
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
|
||||
parent.showSheet = false
|
||||
parent.onEnd()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CustomHostingController<Content: View>: UIHostingController<Content> {
|
||||
override func viewDidLoad() {
|
||||
// view.backgroundColor = .clear
|
||||
// 보여주는 컨트롤러의 프로퍼티 설정 부
|
||||
if let presentationController = presentationController as? UISheetPresentationController {
|
||||
presentationController.detents = [.medium()]
|
||||
// 모달 뷰 위에 작게 바 표시 있는거
|
||||
presentationController.prefersGrabberVisible = false
|
||||
}
|
||||
}
|
||||
}
|
103
JJUNGTABLE/Common/CustomView/ScorllView_Example.swift
Normal file
103
JJUNGTABLE/Common/CustomView/ScorllView_Example.swift
Normal file
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// ScorllView_Example.swift
|
||||
// RememberbyAnything
|
||||
//
|
||||
// Created by Sean Kim on 5/14/24.
|
||||
//
|
||||
import SwiftUI
|
||||
|
||||
struct ScorllView_Example: View {
|
||||
@StateObject private var scrollViewModel: ScrollViewModel = ScrollViewModel()
|
||||
@State var isStartScroll: Bool = true
|
||||
|
||||
// scroll 위치 감지 변경하는 부분
|
||||
private var scrollObservableView: some View {
|
||||
GeometryReader { proxy in
|
||||
let offsetY = proxy.frame(in: .global).origin.y
|
||||
Color.clear
|
||||
.preference(
|
||||
key: ScrollOffsetKey.self,
|
||||
value: offsetY
|
||||
|
||||
)
|
||||
.onAppear {
|
||||
scrollViewModel.setOriginOffset(offsetY)
|
||||
}
|
||||
}
|
||||
.frame(height: 0)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollViewReader { scroll in
|
||||
ScrollView(showsIndicators: false) {
|
||||
scrollObservableView.id(-1)
|
||||
}
|
||||
.onPreferenceChange(ScrollOffsetKey.self) {
|
||||
scrollViewModel.setOffset($0)
|
||||
self.isStartScroll = scrollViewModel.isStartScroll
|
||||
}
|
||||
// 스크롤이 시작할 경우 최상단으로 올리는 버튼 그리기
|
||||
.overlay(alignment: .top) {
|
||||
if isStartScroll {
|
||||
GeometryReader { geo in
|
||||
Button {
|
||||
withAnimation {
|
||||
scroll.scrollTo(-1, anchor: .center)
|
||||
}
|
||||
} label: {
|
||||
Icon.up
|
||||
.font(.nps(size: 30))
|
||||
.foregroundStyle(.brandDeepBlue)
|
||||
.frame(width: geo.size.width, height: isStartScroll ? 30 : 0,alignment: .center)
|
||||
.padding(.init(top: 10, leading: 0, bottom: 20, trailing: 0))
|
||||
.background(
|
||||
LinearGradient(
|
||||
colors: [.whiteSora, .whiteSora.opacity(0)],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// MARK: - 스크롤 뷰 관련 modifier
|
||||
struct HeightPreferenceKey: PreferenceKey {
|
||||
static var defaultValue: CGFloat = 0
|
||||
|
||||
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
||||
value = nextValue()
|
||||
}
|
||||
}
|
||||
|
||||
// 자식뷰에서 상위 뷰로 데이터 전달시 사용 = PreferenceKey
|
||||
struct ScrollOffsetKey: PreferenceKey {
|
||||
static var defaultValue: CGFloat = .zero
|
||||
|
||||
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
||||
value += nextValue()
|
||||
}
|
||||
}
|
||||
|
||||
final class ScrollViewModel: ObservableObject {
|
||||
@Published var offset: CGFloat = 0
|
||||
@Published var isStartScroll: Bool = false
|
||||
var originOffset: CGFloat = 0
|
||||
var isCheckedOriginOffset: Bool = false
|
||||
|
||||
func setOriginOffset(_ offset: CGFloat) {
|
||||
guard !isCheckedOriginOffset else { return }
|
||||
self.originOffset = offset - 30 // 스크롤 패딩 넣은거 만큼 빼야 함
|
||||
self.offset = offset
|
||||
isCheckedOriginOffset = true
|
||||
}
|
||||
|
||||
func setOffset(_ offset: CGFloat) {
|
||||
self.offset = offset
|
||||
if self.offset < self.originOffset { isStartScroll = true }
|
||||
else { isStartScroll = false }
|
||||
}
|
||||
}
|
269
JJUNGTABLE/Common/EDENON.swift
Normal file
269
JJUNGTABLE/Common/EDENON.swift
Normal file
|
@ -0,0 +1,269 @@
|
|||
//
|
||||
// EDENON.swift
|
||||
// JJUNGTABLE
|
||||
//
|
||||
// Created by Sean Kim on 6/13/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
/*
|
||||
암호화 (5단계로 구성)
|
||||
예시)
|
||||
값: 12345678
|
||||
키: 20240612
|
||||
|
||||
0단계
|
||||
암호화 시킬 번호와 키의 자리수가 둘이 같은지 확인
|
||||
|
||||
1단계
|
||||
암호화 시킬 값과 키를 자리수 대로 덧셈을 해준다.
|
||||
- 만일 1자리 수인 경우에는 앞에 0을 붙여서 표시한다.
|
||||
예시)
|
||||
1 2 3 4 5 6 7 8
|
||||
2 0 2 4 0 6 1 2
|
||||
|
||||
결과: 03 02 05 08 05 12 08 10
|
||||
|
||||
2단계
|
||||
1단계의 결과(길이: n)에서 1번째와 n번째를 묶고 2번째와 n-1번째 3번째와 n-2번째를 중앙이 될 때까지 그룹을 만들어 준다.
|
||||
예시)
|
||||
03 02 05 08 05 12 08 10
|
||||
|
||||
결과: 0310 0208 0512 0805
|
||||
|
||||
3단계
|
||||
2단계의 결과에서 나타난 그룹들을 순서에 맞게 특정한 동작을 수행한다.
|
||||
첫번째 그룹은 1번째 항목의 숫자를 떼서 남아있는 숫자의 뒤에 붙이고,
|
||||
두번째 그룹은 1~2번째 항목의 숫자를 떼서 남아있는 숫자의 뒤에 붙이고,
|
||||
세번째 그룹은 1~3번째 항목을 떼서 남아있는 숫자의 뒤에 붙인다.
|
||||
네번째 그룹은 1~4번째 항목을 떼서 남아있는 숫자의 뒤에 붙인다.
|
||||
예시)
|
||||
0310 0208 0512 0805
|
||||
첫번째: 0310 -> 0 을 뗀다 -> 남아있는 수: 310 -> 뒤에 붙인다 = 3100
|
||||
두번째: 0208 -> 02 를 뗀다 -> 남아있는 수: 08 -> 뒤에 붙인다 = 0802
|
||||
세번쨰: 0512 -> 051 을 뗀다 -> 남아있는 수: 2 -> 뒤에 붙인다 = 2051
|
||||
네번째: 0805 -> 0805 를 뗀다 -> 남아있는 수: -> 뒤에 붙인다 = 0805
|
||||
|
||||
결과: 3100 0802 2051 0805
|
||||
|
||||
4단계
|
||||
3단계의 결과에서 각 그룹의 숫자들을 위치를 맞춰 다시 그룹화를 진행한다.
|
||||
|
||||
예시)
|
||||
3100 0802 2051 0805
|
||||
[새 그룹]
|
||||
첫번째: 3 0 2 0
|
||||
두번째: 1 8 0 8
|
||||
세번째: 0 0 5 0
|
||||
네번째: 0 2 1 5
|
||||
|
||||
결과: 3020 1808 0050 0215
|
||||
|
||||
5단계
|
||||
4단계의 결과에서 진행을 하는데 이해를 위해서 변수(isToggle: default = false)를 하나 만들어서 설명하겠다.
|
||||
4단계의 결과에서 숫자를 영문자로 변환하는 작업을 진행한다.
|
||||
1 = A, 2 = B, 3 = C, 4 = D, 5 = E, 6 = F, 7 = G, 8 = H, 9 = I 에 맞춰서 바꿔주는데 isToggle의 값이 false면 대문자로 true이면 소문자로 변환을 해준다.
|
||||
|
||||
하지만 0을 만나게 되면 isToggle의 Bool값을 바꿔주며 0으로 이루어진 그룹이 존재 할경우 1부터 9에 치환되는 알파벳이 아닌 다른 알파벳 (J~Z)를 대문자로 표시해준다.
|
||||
|
||||
만약 0이 연속되는 숫자가 나오게 될경우 2자리 이상일 경우에는 0의 갯수를 표시해준다. 4자리인 경우에는 앞의 예외를 처리 한다.
|
||||
|
||||
예시)
|
||||
3020 1808 0050 0215
|
||||
숫자 - 출력값 - isToggle
|
||||
첫번째
|
||||
3 - C - F
|
||||
0 - - T
|
||||
2 - b - T
|
||||
0 - - F
|
||||
첫번째 그룹이 0000 이 아니므로 다음 작업 진행
|
||||
만약 0000일 경우에 (J~Z)중 랜덤하게 아무 값을 넣는다
|
||||
|
||||
두번쨰
|
||||
1 - A - F
|
||||
8 - H - F
|
||||
0 - - T
|
||||
8 - h - T
|
||||
두번째 그룹이 0000 이 아니므로 다음 작업 진행
|
||||
만약 0000일 경우에 (J~Z)중 랜덤하게 아무 값을 넣는다
|
||||
|
||||
세번째
|
||||
0 - - F
|
||||
0 - 2 - T - 이때 0이 2번 반복 되었으므로 2를 표시
|
||||
5 - e - T
|
||||
0 - - F
|
||||
세번째 그룹이 0000 이 아니므로 다음 작업 진행
|
||||
만약 0000일 경우에 (J~Z)중 랜덤하게 아무 값을 넣는다
|
||||
|
||||
네번째
|
||||
0 - 2 - T - 세번째 그룹의 마지막에서도 0이 나와서 이번까지 0이 2번 반복되었기에 2를 표시
|
||||
2 - b - T
|
||||
1 - a - T
|
||||
5 - e - T
|
||||
네번째 그룹이 0000 이 아니므로 다음 작업 진행
|
||||
만약 0000일 경우에 (J~Z)중 랜덤하게 아무 값을 넣는다
|
||||
|
||||
결과: Cb AHh 2e 2bae
|
||||
|
||||
|
||||
이렇게 진행한다.
|
||||
값: 12345678
|
||||
키: 20240612
|
||||
결과: Cb AHh 2e 2bae
|
||||
|
||||
*/
|
||||
|
||||
struct ENDC_ERROR:Error {
|
||||
var caseType: CASE
|
||||
var message: String
|
||||
|
||||
enum CASE: Error{
|
||||
case EN_DIGIT
|
||||
case DC_WRONG
|
||||
}
|
||||
}
|
||||
|
||||
func edon_E(value: String, key: String) -> Future<String, ENDC_ERROR> {
|
||||
return Future { promise in
|
||||
var result = ""
|
||||
|
||||
// 0단계: 자리수 확인
|
||||
if value.count != key.count { promise(.failure(ENDC_ERROR(caseType: .EN_DIGIT, message: "digit ERROR"))) }
|
||||
|
||||
/// 1단계: 자리별 덧셈 + 그룹화 + 셔플
|
||||
/// <순서>
|
||||
/// 1. 암호화 할 값과 키를 자리 별로 덧셈을 진행
|
||||
/// 2. 1의 결과가 1자리일 경우에는 앞에 0을 붙여서 2자리로 만들어줌
|
||||
/// 3. 0 ~ index 까지의 배열을 [index+1... end] 뒤에 붙여준다.
|
||||
/// 4. 3의 결과로 만들어진 그룹에서 각 자리 위치에 있는 숫자들로 새로운 그룹을 만들어준다.
|
||||
/// 5. 4의 결과를 가지고 하나의 String 값으로 만들어 반환한다.
|
||||
let sum = { () -> String in
|
||||
var tempArray = ["","","",""]
|
||||
let add = zip(value, key).map{
|
||||
guard let first = Int(String($0)), let second = Int(String($1)) else { return ""}
|
||||
return String(format: "%02d", first + second)
|
||||
}
|
||||
|
||||
zip(add[...3],add[4...].reversed())
|
||||
.map{"\($0)\($1)"}.enumerated()
|
||||
.map { (index, value) in
|
||||
let sumValue = value.map{$0}
|
||||
return "\(String(sumValue[(index+1)...]))\(String(sumValue[...(index)]))"
|
||||
}
|
||||
.joined(separator: "")
|
||||
.enumerated().map { (index, value) in
|
||||
tempArray[index % 4] += "\(value)"
|
||||
}
|
||||
// print(tempArray)
|
||||
// print(tempArray.joined(separator: ""))
|
||||
return tempArray.joined(separator: "")
|
||||
}()
|
||||
|
||||
// print(sum)
|
||||
|
||||
// print(String(Character(UnicodeScalar(73))))
|
||||
//"I".unicodeScalars.first?.value
|
||||
|
||||
/// 2단계
|
||||
/// [영문자 아스키 코드 : 65~73]
|
||||
/// <순서>
|
||||
/// 1.
|
||||
var isToggle = false
|
||||
var zCnt = 0
|
||||
|
||||
let ascii = { (value: Int) -> String in
|
||||
if let scalar = UnicodeScalar(value) { return "\(scalar)" }
|
||||
return ""
|
||||
}
|
||||
|
||||
for value in sum {
|
||||
if value == "0" {
|
||||
isToggle.toggle()
|
||||
zCnt += 1
|
||||
if zCnt == 4 {
|
||||
result += isToggle ? ascii(Int.random(in: 74...90)).lowercased() : ascii(Int.random(in: 74...90))
|
||||
zCnt = 0
|
||||
}
|
||||
}
|
||||
else {
|
||||
if zCnt > 1 { result += "\(zCnt)" }
|
||||
zCnt = 0
|
||||
result += isToggle ? ascii(Int("\(value)")!+64).lowercased() : ascii(Int("\(value)")!+64)
|
||||
}
|
||||
}
|
||||
promise(.success(result))
|
||||
}
|
||||
}
|
||||
func edon_D(value: String, key: String) -> Future<String, ENDC_ERROR> {
|
||||
return Future { promise in
|
||||
var result = ""
|
||||
var temp = ""
|
||||
var isToggle = false
|
||||
|
||||
let ascii = { (value: String) -> Int in
|
||||
if let scalar = value.unicodeScalars.first, let sInt = Int("\(scalar.value)") { return sInt }
|
||||
return 0
|
||||
}
|
||||
|
||||
// 대 = 65~73, 소 = 97~105
|
||||
value.map {
|
||||
if let codeNum = Int("\($0)") {
|
||||
for _ in 0..<codeNum {
|
||||
temp += "0"
|
||||
isToggle.toggle()
|
||||
}
|
||||
}
|
||||
else {
|
||||
let code = ascii("\($0)")
|
||||
|
||||
if code > 64 && code < 74 {
|
||||
if isToggle {
|
||||
temp += "0\(code-64)"
|
||||
isToggle.toggle()
|
||||
}
|
||||
else { temp += "\(code-64)" }
|
||||
}
|
||||
else if code > 96 && code < 106 {
|
||||
|
||||
if !isToggle {
|
||||
temp += "0\(code-96)"
|
||||
isToggle.toggle()
|
||||
}
|
||||
else { temp += "\(code-96)" }
|
||||
}
|
||||
else {
|
||||
temp += "0000"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if temp.count % 4 > 0 { for _ in 0 ..< 4-(temp.count%4) { temp += "0" } }
|
||||
|
||||
var tempArray = ["","","",""]
|
||||
temp.enumerated().map {(index,value) in tempArray[index%4] += "\(value)" }
|
||||
|
||||
var saveArray = [String].init(repeating: "", count: 8)
|
||||
var saveTArray = [[String]]()
|
||||
|
||||
|
||||
tempArray.enumerated().map {(index,value) in
|
||||
let sepValue = value.map{$0}
|
||||
|
||||
saveTArray.append("\(String(sepValue[(3-index)...]))\(String(sepValue[...(3-index-1)]))".map{"\($0)"})
|
||||
|
||||
saveArray[index] = saveTArray[index][0...1].joined(separator: "")
|
||||
saveArray[(7-index)] = saveTArray[index][2...3].joined(separator: "")
|
||||
}
|
||||
|
||||
for (v,k) in zip(saveArray,key) {
|
||||
if let value = Int(v), let key = Int("\(k)") {
|
||||
if value-key > 0 { result += "\(value-key)" }
|
||||
else { result += "\((value-key) * -1)" }
|
||||
}
|
||||
}
|
||||
|
||||
promise(.success(result))
|
||||
}
|
||||
}
|
47
JJUNGTABLE/Common/FB_Functions.swift
Normal file
47
JJUNGTABLE/Common/FB_Functions.swift
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// FB_Functions.swift
|
||||
// CheckAnything
|
||||
//
|
||||
// Created by Sean Kim on 5/23/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import FirebaseMessaging
|
||||
import FirebaseAuth
|
||||
|
||||
class FB_FUNC {
|
||||
static var shared = FB_FUNC()
|
||||
private init() {}
|
||||
|
||||
//CheckAnything.com
|
||||
func sendPushNotification(_ title: String, body: String) {
|
||||
@UserDefault (key: "fcmToken", defaultValue: "") var pushFCMToken
|
||||
|
||||
// let url = URL(string: "\(KEY.apiLink)send?&")!
|
||||
// var request = URLRequest(url: url)
|
||||
// request.httpMethod = "GET"
|
||||
// let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
||||
// if let error = error {
|
||||
// print("Error sending FCM token: \(error)")
|
||||
// return
|
||||
// }
|
||||
// if let response = response as? HTTPURLResponse, response.statusCode == 200 {
|
||||
// print("FCM token sent successfully")
|
||||
// } else {
|
||||
// print("Failed to send FCM token")
|
||||
// }
|
||||
// }
|
||||
// task.resume()
|
||||
}
|
||||
|
||||
|
||||
func login(_ id: String, _ password: String, _ code: String) {
|
||||
Auth.auth().signIn(withEmail: "\(id)@checkanythingemail.com", password: password) { authResult, error in
|
||||
if let error = error {
|
||||
printLog(error.localizedDescription)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
190
JJUNGTABLE/Common/Login.swift
Normal file
190
JJUNGTABLE/Common/Login.swift
Normal file
|
@ -0,0 +1,190 @@
|
|||
//
|
||||
// Login.swift
|
||||
// JJUNGTABLE
|
||||
//
|
||||
// Created by Sean Kim on 6/11/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
import AuthenticationServices
|
||||
|
||||
import KakaoSDKCommon
|
||||
import KakaoSDKAuth
|
||||
import KakaoSDKUser
|
||||
|
||||
import Firebase
|
||||
import FirebaseAuth
|
||||
|
||||
/*
|
||||
|
||||
2024-06-11 01:26:08184 [ℹ️][Api.swift 136:41] -> response:
|
||||
Optional({
|
||||
"app_id" = 987284;
|
||||
"expires_in" = 43199;
|
||||
id = 3138117983;
|
||||
})
|
||||
__________ __________ __________ __________
|
||||
* LOCATION : [ContentView.swift : 69] - body
|
||||
| TIME : [2024/06/12 14:39:17:856]
|
||||
> NOTE : 001273.49bfcb15b70d440a9666b4434336fc2c.0741
|
||||
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
|
||||
*/
|
||||
|
||||
|
||||
|
||||
class Login: NSObject, ObservableObject{
|
||||
// let appId = "987284"
|
||||
|
||||
@State private var verificationCode: String = ""
|
||||
@State private var verificationID: String?
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var currentPromise: Future<String, LOGIN_ERROR>.Promise?
|
||||
|
||||
func tryKakaoLogin() -> Future<String, LOGIN_ERROR> {
|
||||
return Future { promise in
|
||||
if UserApi.isKakaoTalkLoginAvailable() { // 카카오 간편로그인이 가능한지 확인
|
||||
UserApi.shared.loginWithKakaoTalk { (oauthToken, error) in
|
||||
if let error = error {
|
||||
promise(.failure(.init(caseType: .KAKAO_LOGIN,
|
||||
message: "\(error.localizedDescription)")))
|
||||
} else {
|
||||
printLog("Login success.: \(oauthToken)")
|
||||
self.kakaoAccessUserInfo().sink(
|
||||
receiveCompletion: { completion in
|
||||
if case .failure(let error) = completion {
|
||||
promise(.failure(error))
|
||||
}
|
||||
},
|
||||
receiveValue: { id in
|
||||
promise(.success(id))
|
||||
}
|
||||
).store(in: &self.cancellables)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
UserApi.shared.loginWithKakaoAccount { (oauthToken, error) in
|
||||
if let error = error {
|
||||
promise(.failure(.init(caseType: .KAKAO_LOGIN,
|
||||
message: "\(error.localizedDescription)")))
|
||||
} else {
|
||||
print("Login success.")
|
||||
// AccessToken 저장 또는 다른 로그인 후 처리
|
||||
self.kakaoAccessUserInfo().sink(
|
||||
receiveCompletion: { completion in
|
||||
if case .failure(let error) = completion {
|
||||
promise(.failure(error))
|
||||
}
|
||||
},
|
||||
receiveValue: { id in
|
||||
promise(.success(id))
|
||||
}
|
||||
).store(in: &self.cancellables)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func kakaoAccessUserInfo() -> Future<String, LOGIN_ERROR> {
|
||||
return Future { promise in
|
||||
UserApi.shared.me { user, error in
|
||||
if let error = error {
|
||||
printLog("[ERROR] Kakao me: \(error)")
|
||||
promise(.failure(.init(caseType: .KAKAO_ACC_USER,
|
||||
message: "\(error.localizedDescription)")))
|
||||
}
|
||||
else if let user = user, let id = user.id {
|
||||
printLog("Access Kakao UserInfo")
|
||||
promise(.success("\(id)"))
|
||||
}
|
||||
else {
|
||||
promise(.failure(.init(caseType: .KAKAO_ACC_USER,
|
||||
message: "undefined ERROR")))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
extension Login: ASAuthorizationControllerDelegate {
|
||||
func tryAppleLogin() -> Future<String, LOGIN_ERROR> {
|
||||
return Future { promise in
|
||||
let request = ASAuthorizationAppleIDProvider().createRequest()
|
||||
// request.requestedScopes = [.fullName, .email]
|
||||
|
||||
let controller = ASAuthorizationController(authorizationRequests: [request])
|
||||
controller.delegate = self
|
||||
controller.presentationContextProvider = self
|
||||
// 이거를 써야지 로그인 창이 뜹니다
|
||||
controller.performRequests()
|
||||
|
||||
self.currentPromise = promise
|
||||
}
|
||||
}
|
||||
|
||||
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
|
||||
printLog("APPLE success")
|
||||
if let credential = authorization.credential as? ASAuthorizationAppleIDCredential {
|
||||
let userId = credential.user
|
||||
currentPromise?(.success(userId))
|
||||
} else {
|
||||
currentPromise?(.failure(.init(caseType: .APPLE_LOGIN, message: "로그인 실패")))
|
||||
}
|
||||
currentPromise = nil
|
||||
}
|
||||
|
||||
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: any Error) {
|
||||
currentPromise?(.failure(.init(caseType: .APPLE_LOGIN, message: "\(error.localizedDescription)")))
|
||||
currentPromise = nil
|
||||
}
|
||||
}
|
||||
|
||||
extension Login: ASAuthorizationControllerPresentationContextProviding{
|
||||
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
|
||||
|
||||
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
|
||||
return ASPresentationAnchor()
|
||||
}
|
||||
|
||||
// Return the first window of the window scene
|
||||
return windowScene.windows.first { $0.isKeyWindow } ?? ASPresentationAnchor()
|
||||
}
|
||||
}
|
||||
|
||||
extension Login {
|
||||
|
||||
|
||||
func sendCodeToPhone() {
|
||||
PhoneAuthProvider.provider().verifyPhoneNumber("+821034523551", uiDelegate: nil) { verificationID, error in
|
||||
if let error = error {
|
||||
print("Error: \(error.localizedDescription)")
|
||||
return
|
||||
}
|
||||
|
||||
// self.verificationID = verificationID
|
||||
UserDefaults.standard.set(verificationID, forKey: "authVerifyID")
|
||||
printLog(verificationID)
|
||||
}
|
||||
}
|
||||
|
||||
func verifyCode(code: String) {
|
||||
printLog(code)
|
||||
// let verificationID = UserDefaults.standard.string(forKey: "authVerifyID")
|
||||
guard let verificationID = UserDefaults.standard.string(forKey: "authVerifyID") else {
|
||||
printLog("Please request verification code first.")
|
||||
return
|
||||
}
|
||||
|
||||
let credential = PhoneAuthProvider.provider().credential(
|
||||
withVerificationID: verificationID,
|
||||
verificationCode: code
|
||||
)
|
||||
|
||||
|
||||
printLog(credential)
|
||||
}
|
||||
|
||||
}
|
55
JJUNGTABLE/Common/Modifier/BaseViewModifier.swift
Normal file
55
JJUNGTABLE/Common/Modifier/BaseViewModifier.swift
Normal file
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// BaseViewModifier.swift
|
||||
// JJUNGTABLE
|
||||
//
|
||||
// Created by Sean Kim on 6/11/24.
|
||||
//
|
||||
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
|
||||
struct BaseViewModifier: ViewModifier {
|
||||
@ObservedObject private var networkMonitor = NetworkMonitor.shared
|
||||
@EnvironmentObject var viewModel: ViewModel
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
// 네트워크 끊기게 되면 여기서 처리 함
|
||||
.onChange(of: networkMonitor.isConnected) { isConnectd in
|
||||
if !isConnectd {
|
||||
viewModel.alertData = .init(body: """
|
||||
네트워크 문제가 발생하였습니다.
|
||||
확인 후 다시 실행해주세요.
|
||||
""",
|
||||
button: [ButtonType(name: "확인", role: .none ,
|
||||
function: {exit(1)})])
|
||||
viewModel.showAlert.toggle()
|
||||
}
|
||||
}
|
||||
// 알럿은 공통단으로 다음과 같이 구성을 해놓을 거임
|
||||
.alert(viewModel.alertData.title,
|
||||
isPresented: $viewModel.showAlert,
|
||||
presenting: $viewModel.alertData) { data in
|
||||
let count = data.button.count
|
||||
ForEach(0 ..< count, id: \.self) { index in
|
||||
let btn = data.wrappedValue.button[index]
|
||||
Button(role: btn.role) {
|
||||
printLog(btn)
|
||||
if let function = btn.function { function() }
|
||||
} label: {
|
||||
Text("\(btn.name)")
|
||||
}
|
||||
}
|
||||
} message: { data in
|
||||
Text("\(data.body.wrappedValue)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func setBaseViewModifier() -> some View {
|
||||
self.modifier(BaseViewModifier())
|
||||
}
|
||||
}
|
27
JJUNGTABLE/Common/Modifier/NetworkMonitor.swift
Normal file
27
JJUNGTABLE/Common/Modifier/NetworkMonitor.swift
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// NetworkMonitor.swift
|
||||
// JJUNGTABLE
|
||||
//
|
||||
// Created by Sean Kim on 6/11/24.
|
||||
//
|
||||
|
||||
import Network
|
||||
import Combine
|
||||
|
||||
class NetworkMonitor: ObservableObject {
|
||||
static let shared = NetworkMonitor()
|
||||
|
||||
private let monitor = NWPathMonitor()
|
||||
private let queue = DispatchQueue.global(qos: .background)
|
||||
|
||||
@Published var isConnected: Bool = true
|
||||
|
||||
private init() {
|
||||
monitor.pathUpdateHandler = { [weak self] path in
|
||||
DispatchQueue.main.async {
|
||||
self?.isConnected = (path.status == .satisfied)
|
||||
}
|
||||
}
|
||||
monitor.start(queue: queue)
|
||||
}
|
||||
}
|
139
JJUNGTABLE/Common/Modifier/SwiftUI_Modifier.swift
Normal file
139
JJUNGTABLE/Common/Modifier/SwiftUI_Modifier.swift
Normal file
|
@ -0,0 +1,139 @@
|
|||
//
|
||||
// SwiftUI_Modifier.swift
|
||||
// PersonalHealthDiary
|
||||
//
|
||||
// Created by Sean Kim on 2/20/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
|
||||
extension View {
|
||||
func makeSystemButton(_ systemName: String, text: String = "",color: Color = .black, img:CGFloat = 40,size: CGFloat = 20) -> some View {
|
||||
modifier(BottomModifier(systemName: systemName,color: color,text: text, fontSize: size, imgSize: img))
|
||||
}
|
||||
|
||||
func fullPage(_ backColor: Color) -> some View {
|
||||
self
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(backColor)
|
||||
}
|
||||
|
||||
func font(_ type: FontType)-> some View {
|
||||
switch type {
|
||||
case .Title:
|
||||
return AnyView(self.modifier(TitleFont()))
|
||||
case .Content:
|
||||
return AnyView(self.modifier(ContentFont()))
|
||||
case .Small:
|
||||
return AnyView(self.modifier(SmallFont()))
|
||||
}
|
||||
}
|
||||
}
|
||||
#if canImport(UIKit)
|
||||
extension View {
|
||||
func hideKeyboard() {
|
||||
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct BottomModifier: ViewModifier {
|
||||
var systemName: String
|
||||
var color: Color
|
||||
var text: String
|
||||
var fontSize: CGFloat
|
||||
var imgSize: CGFloat
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
VStack {
|
||||
Image(systemName: systemName)
|
||||
.resizable().aspectRatio(contentMode: .fill)
|
||||
.tint(color)
|
||||
.frame(width: imgSize ,height: imgSize)
|
||||
.padding(.init(top: 0, leading: 10, bottom: 0, trailing: 10))
|
||||
if text != "" {
|
||||
Text(text)
|
||||
.tint(color)
|
||||
.font(.system(size: fontSize))
|
||||
.padding(.init(top: 0, leading: 0, bottom: 5, trailing: 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct MenuButtonBack: ViewModifier {
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.font(.nps(size: 24))
|
||||
.tint(.brand)
|
||||
.frame(width: 40, height: 40, alignment: .center)
|
||||
.background{
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.strokeBorder(.brand,
|
||||
style: StrokeStyle(lineWidth: 2))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct TitleFont: ViewModifier {
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.font(.nps(font: .bold, size: 32))
|
||||
.minimumScaleFactor(0.1)
|
||||
.tint(.brand)
|
||||
.frame(maxHeight: 32, alignment: .leading)
|
||||
}
|
||||
}
|
||||
|
||||
// Title 보다 40% 감소
|
||||
struct ContentFont: ViewModifier {
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.font(.nps(size: 20))
|
||||
.minimumScaleFactor(0.1)
|
||||
.tint(.brand)
|
||||
.frame(maxHeight: 20, alignment: .leading)
|
||||
}
|
||||
}
|
||||
|
||||
// Content 보다 30% 감소
|
||||
struct SmallFont: ViewModifier {
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.font(.nps(size: 14))
|
||||
.minimumScaleFactor(0.1)
|
||||
.tint(.brand)
|
||||
.frame(maxHeight: 14, alignment: .leading)
|
||||
}
|
||||
}
|
||||
|
||||
struct AlignmentView: ViewModifier {
|
||||
enum AlignmentType {
|
||||
case leading
|
||||
case trailing
|
||||
case top
|
||||
case bottom
|
||||
}
|
||||
|
||||
var type: AlignmentType
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
if type == .leading || type == .trailing {
|
||||
HStack(spacing: 0) {
|
||||
if type == .trailing { Spacer() }
|
||||
content
|
||||
if type == .leading { Spacer() }
|
||||
}
|
||||
}
|
||||
else {
|
||||
VStack(spacing: 0) {
|
||||
if type == .bottom { Spacer() }
|
||||
content
|
||||
if type == .top { Spacer() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
448
JJUNGTABLE/Common/Modifier/SwiftUI_Prefix.swift
Normal file
448
JJUNGTABLE/Common/Modifier/SwiftUI_Prefix.swift
Normal file
|
@ -0,0 +1,448 @@
|
|||
//
|
||||
// SwiftUI_Prefix.swift
|
||||
// PersonalHealthDiary
|
||||
//
|
||||
// Created by Sean Kim on 2/20/24.
|
||||
//
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - TYPEALIAS
|
||||
typealias VOID_TO_VOID = () -> ()
|
||||
|
||||
// MARK: - VARIABLE
|
||||
public var APPSTORE_URL = "https://itunes.apple.com/app/"
|
||||
public var KEYBOARD_UP_HEIGHT: CGFloat = 46.0
|
||||
|
||||
enum Compare: Int{
|
||||
case bigger = 0
|
||||
case smaller
|
||||
case equal
|
||||
case error
|
||||
}
|
||||
|
||||
// MARK: - FUNCTION
|
||||
/// print 를 조금 더 자세하게 표기해주는 함수
|
||||
public func printLog<T>(_ object: T, _ file: String = #file, _ function: String = #function, _ line: Int = #line){
|
||||
#if DEBUG
|
||||
let dateString = Date().convertString("yyyy/MM/dd HH:mm:ss:SSS")
|
||||
Swift.print(
|
||||
// """
|
||||
// __________ __________
|
||||
// |* TIME = [\(dateString)] || FILE = [\(file.lastPathComponent)]
|
||||
// | NAME = [\(function)] || LINE = [\(line)]
|
||||
// |>>> PRINT = \(object)
|
||||
//  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
|
||||
// """
|
||||
"""
|
||||
__________ __________ __________ __________
|
||||
* LOCATION : [\(file.lastPathComponent) : \(line)] - \(function)
|
||||
| TIME : [\(dateString)]
|
||||
> NOTE : \(object)
|
||||
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
|
||||
"""
|
||||
)
|
||||
#else
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
public func getDeviceWidth() -> CGFloat { UIScreen.main.bounds.size.width }
|
||||
public func getDeviceHeight() -> CGFloat { UIScreen.main.bounds.size.height }
|
||||
|
||||
|
||||
/// 탈옥 여부 파악 함수
|
||||
/// false 떠야 정상적인것
|
||||
public func isIllegalDevice() -> Bool {
|
||||
func canOpen(path: String) -> Bool {
|
||||
let file = fopen(path, "r")
|
||||
guard file != nil else { return false }
|
||||
fclose(file)
|
||||
return true
|
||||
}
|
||||
|
||||
guard let cydiaUrlScheme = NSURL(string: "cydia://package/com.example.package") else { return false }
|
||||
if UIApplication.shared.canOpenURL(cydiaUrlScheme as URL) {
|
||||
return true
|
||||
}
|
||||
|
||||
#if arch(i386) || arch(x86_64)
|
||||
return false
|
||||
#endif
|
||||
|
||||
let fileManager = FileManager.default
|
||||
if fileManager.fileExists(atPath: "/Applications/Cydia.app") ||
|
||||
fileManager.fileExists(atPath: "/Library/MobileSubstrate/MobileSubstrate.dylib") ||
|
||||
fileManager.fileExists(atPath: "/bin/bash") ||
|
||||
fileManager.fileExists(atPath: "/usr/sbin/sshd") ||
|
||||
fileManager.fileExists(atPath: "/etc/apt") ||
|
||||
fileManager.fileExists(atPath: "/usr/bin/ssh") ||
|
||||
fileManager.fileExists(atPath: "/private/var/lib/apt") {
|
||||
return true
|
||||
}
|
||||
if canOpen(path: "/Applications/Cydia.app") ||
|
||||
canOpen(path: "/Library/MobileSubstrate/MobileSubstrate.dylib") ||
|
||||
canOpen(path: "/bin/bash") ||
|
||||
canOpen(path: "/usr/sbin/sshd") ||
|
||||
canOpen(path: "/etc/apt") ||
|
||||
canOpen(path: "/usr/bin/ssh") {
|
||||
return true
|
||||
}
|
||||
let path = "/private/" + NSUUID().uuidString
|
||||
do {
|
||||
try "anyString".write(toFile: path, atomically: true, encoding: String.Encoding.utf8)
|
||||
try fileManager.removeItem(atPath: path)
|
||||
return true
|
||||
} catch let error { // 이 부분
|
||||
printLog("Jail ERROR: \(error))")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public func fontNameCheck() {
|
||||
for family: String in UIFont.familyNames {
|
||||
print(family)
|
||||
for names : String in UIFont.fontNames(forFamilyName: family){
|
||||
printLog("\(names)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// a 가 b 보다 클 경우 true 반환 ( 그외 전부 false)
|
||||
public func isBigger (_ a: Int, _ b: Int) -> Bool {
|
||||
if a > b { return true } else { return false }
|
||||
}
|
||||
|
||||
/// JSON 형태의 String 을 받아서 Dictionary 의 형태로 내보냄
|
||||
public func jsonToDict(_ input: String) -> [String: Any] {
|
||||
if let jsonData = input.data(using: .utf8){
|
||||
do {
|
||||
if let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
|
||||
return jsonObject
|
||||
}
|
||||
} catch let error { // 이 부분
|
||||
printLog("JSON ERROR: \(error))")
|
||||
}
|
||||
}
|
||||
return [:]
|
||||
}
|
||||
|
||||
func currentVersion() -> String {
|
||||
guard let currentVer = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { return "" }
|
||||
return currentVer
|
||||
|
||||
}
|
||||
|
||||
func versionChange(ver: String) -> [Int] {
|
||||
return ver.components(separatedBy: ["."]).map {Int($0) ?? 0}
|
||||
}
|
||||
|
||||
|
||||
/// a 가 b 보다 OOO 하다.
|
||||
func compareVersion(_ a: String, _ b: String) -> Compare {
|
||||
let aList = versionChange(ver: a)
|
||||
let bList = versionChange(ver: b)
|
||||
if aList.count != bList.count { return .error }
|
||||
else {
|
||||
for i in 0 ..< aList.count {
|
||||
if aList[i] > bList[i] { return .bigger }
|
||||
else if aList[i] < bList[i] { return .smaller }
|
||||
}
|
||||
return .equal
|
||||
}
|
||||
}
|
||||
|
||||
func copyToClipboard(_ text: String){
|
||||
UIPasteboard.general.string = text
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - CUSTOM COMPONENTS
|
||||
|
||||
|
||||
// MARK: - EXTENSION
|
||||
extension String {
|
||||
/// 마지막 경로 구성 요소
|
||||
var lastPathComponent: String {
|
||||
get {
|
||||
return (self as NSString).lastPathComponent
|
||||
}
|
||||
}
|
||||
|
||||
/// 원하는 길이 만큼 절단
|
||||
func cut(start: Int, end: Int) -> String {
|
||||
let startIndex = self.index(self.startIndex,offsetBy: start >= 0 ? start : 0)
|
||||
let endIndex = self.index(self.startIndex,offsetBy: end >= 0 ? end : 0)
|
||||
let result: String = "\(self[startIndex ..< endIndex])"
|
||||
return result
|
||||
}
|
||||
|
||||
/// 글자 하나 단위로 구분
|
||||
func letter() -> [String] {
|
||||
return self.map { String($0) }
|
||||
}
|
||||
|
||||
/// 단어 하나 단위로 구문 (띄어쓰기)
|
||||
func word() -> [String] {
|
||||
return self.components(separatedBy: " ")
|
||||
}
|
||||
|
||||
/// Date() 로 변환 -> (Bool, Date) 출력
|
||||
func convertDate(_ dateFormat: String = "yyyyMMdd") -> (Bool, Date) {
|
||||
let dateFormatter = Date().setDateFormatter(dateFormat)
|
||||
if let convert = dateFormatter.date(from: self) {
|
||||
return (true, convert)
|
||||
} else {
|
||||
return (false, Date())
|
||||
}
|
||||
}
|
||||
|
||||
/// 정규식 체크
|
||||
///
|
||||
/// 이메일 정규식 예시: "[A-Z0-9a-z._%+-]+@[A-Z0-9a-z._%+-]+\\.[A-Za-z]{2,64}"
|
||||
/// 비밀번호 정규식 에시: "[A-Z0-9a-z._%+-]{6,12}"
|
||||
func checkFilter(_ filter: String) -> Bool {
|
||||
if self == "" { return false }
|
||||
return self.range(of: filter, options: .regularExpression) != nil
|
||||
|
||||
}
|
||||
|
||||
/// Bool 값에 맞게 DateFormat을 반환
|
||||
static func makeDateFormat(year: Bool, month: Bool, day: Bool, dayOfWeek: Bool = false, am_pm: Bool = false, hour: Bool = false,_ fullTime: Bool = true, minute: Bool = false, second: Bool = false, mSecond: Bool = false, mSDigit: Int = 1) -> String {
|
||||
|
||||
var dateFormat = ""
|
||||
|
||||
if year {
|
||||
dateFormat = dateFormat + "yyyy"
|
||||
}
|
||||
if month {
|
||||
dateFormat = dateFormat + "MM"
|
||||
}
|
||||
if day {
|
||||
dateFormat = dateFormat + "dd"
|
||||
}
|
||||
if dayOfWeek {
|
||||
dateFormat = dateFormat + "EEEEEE"
|
||||
}
|
||||
|
||||
if am_pm {
|
||||
dateFormat = dateFormat + "a"
|
||||
}
|
||||
|
||||
if fullTime { // 24시간
|
||||
if hour {
|
||||
dateFormat = dateFormat + "HH"
|
||||
}
|
||||
} else { // 12시간
|
||||
if hour {
|
||||
dateFormat = dateFormat + "hh"
|
||||
}
|
||||
}
|
||||
|
||||
if minute {
|
||||
dateFormat = dateFormat + "mm"
|
||||
}
|
||||
|
||||
if second {
|
||||
dateFormat = dateFormat + "ss"
|
||||
}
|
||||
|
||||
if mSecond {
|
||||
for _ in 0 ..< mSDigit {
|
||||
dateFormat = dateFormat + "S"
|
||||
}
|
||||
}
|
||||
|
||||
return dateFormat
|
||||
}
|
||||
|
||||
/// 날짜를 합쳐서 하나의 String으로 반환
|
||||
static func combineDate(year: Int, month: Int, day: Int) -> String {
|
||||
return "\(year * 10000 + month * 100 + day)"
|
||||
}
|
||||
|
||||
func stringToInt() -> Int {
|
||||
if let intValue = Int(self) {
|
||||
return intValue
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Date {
|
||||
/// 연도 표시
|
||||
///
|
||||
/// 변환에 실패시 현재 날짜의 년 표시
|
||||
var year: Int {
|
||||
let dateFormatter = self.setDateFormatter("yyyy")
|
||||
if let year = Int(dateFormatter.string(from: self)){
|
||||
return year
|
||||
} else {
|
||||
return Calendar.current.component(.year, from: self)
|
||||
}
|
||||
}
|
||||
|
||||
/// 월 표시
|
||||
///
|
||||
/// 변환 실패시 현재 날짜의 월 표시
|
||||
var month: Int {
|
||||
let dateFormatter = self.setDateFormatter("MM")
|
||||
if let month = Int(dateFormatter.string(from: self)) {
|
||||
return month
|
||||
} else {
|
||||
return Calendar.current.component(.month, from: self)
|
||||
}
|
||||
}
|
||||
|
||||
/// 일 표시
|
||||
///
|
||||
/// 변환 실패시 현재 날짜의 월 표시
|
||||
var day: Int {
|
||||
let dateFormatter = self.setDateFormatter("dd")
|
||||
if let day = Int(dateFormatter.string(from: self)) {
|
||||
return day
|
||||
} else {
|
||||
return Calendar.current.component(.day, from: self)
|
||||
}
|
||||
}
|
||||
|
||||
/// 요일 표시
|
||||
///
|
||||
/// 일요일 부터 시작해 0~6까지 반환
|
||||
///
|
||||
/// -1 반환시 오류 발생
|
||||
var dayOfWeek: Int {
|
||||
let dateFormatter = self.setDateFormatter("EEEEEE")
|
||||
let convert = dateFormatter.string(from: self)
|
||||
switch convert {
|
||||
case "일":
|
||||
return 0
|
||||
case "월":
|
||||
return 1
|
||||
case "화":
|
||||
return 2
|
||||
case "수":
|
||||
return 3
|
||||
case "목":
|
||||
return 4
|
||||
case "금":
|
||||
return 5
|
||||
case "토":
|
||||
return 6
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
func setDateFormatter(_ dateFormat: String) -> DateFormatter {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = dateFormat
|
||||
dateFormatter.timeZone = TimeZone(abbreviation: "KST")
|
||||
dateFormatter.locale = Locale(identifier: "ko_kr")
|
||||
return dateFormatter
|
||||
}
|
||||
|
||||
/// String 값으로 변환
|
||||
func convertString(_ dateFormat: String = "yyyyMMdd") -> String {
|
||||
let dateFormatter = self.setDateFormatter(dateFormat)
|
||||
return dateFormatter.string(from: self)
|
||||
}
|
||||
|
||||
|
||||
/// 해당 년도가 윤년인지 확인
|
||||
///
|
||||
/// 값이 정확하지 않으면 현재 날짜로 파악
|
||||
func checkLeapMonth() -> Bool {
|
||||
if self.year % 4 == 0 {
|
||||
if self.year % 100 == 0 {
|
||||
if self.year % 400 == 0 {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/// 해당 월의 날짜 수 확인
|
||||
///
|
||||
/// 값이 정확하지 않으면 현재 날짜로 파악
|
||||
func getLastDayOfMonth() -> Int {
|
||||
switch self.month {
|
||||
case 1,3,5,7,8,10,12 :
|
||||
return 31
|
||||
case 2:
|
||||
if self.checkLeapMonth() {
|
||||
return 29
|
||||
} else {
|
||||
return 28
|
||||
}
|
||||
case 4,6,9,11:
|
||||
return 30
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Font {
|
||||
enum NPS_Font : String {
|
||||
case regular
|
||||
case bold
|
||||
|
||||
var value: String {
|
||||
switch self {
|
||||
case .regular:
|
||||
return "NPS-font-Regular"
|
||||
case .bold:
|
||||
return "NPS-font-Bold"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func nps(font: NPS_Font = .regular, size: CGFloat = 12) -> Font {
|
||||
return .custom(font.value, size: size)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension View {
|
||||
func endTextEditing() {
|
||||
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ANNOTATION
|
||||
|
||||
/// 사용방법
|
||||
/// @UserDefault (key: "keyName", defaultValue: "default") var 변수
|
||||
/// print(변수)
|
||||
/// - 위에처럼 하게 되면 UserDefaults에 저장된 key 값을 갖고있는걸 보여주든가 기본 값 보여줌
|
||||
/// 변수 = 변경값
|
||||
/// - 위에처럼 하게되면 UserDefaults에 해당 key 값에 변경값을 저장하고 들고 있음
|
||||
@propertyWrapper
|
||||
struct UserDefault<T> {
|
||||
private let ud: UserDefaults = .standard
|
||||
private let key: String
|
||||
private var defaultValue: T
|
||||
|
||||
var wrappedValue: T {
|
||||
set { ud.set(newValue, forKey: key) }
|
||||
get { ud.object(forKey: key) as? T ?? defaultValue }
|
||||
}
|
||||
|
||||
init(key: String, defaultValue: T) {
|
||||
self.key = key
|
||||
self.defaultValue = defaultValue
|
||||
}
|
||||
|
||||
///사용법: _변수.removeData()
|
||||
/// - 중요사항 변수 앞에 _ 무조건 붙여야 함
|
||||
func removeData() {
|
||||
ud.removeObject(forKey: key)
|
||||
}
|
||||
}
|
28
JJUNGTABLE/Common/Prj_Common/KEY.swift
Normal file
28
JJUNGTABLE/Common/Prj_Common/KEY.swift
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// KEY.swift
|
||||
// JJUNGTABLE
|
||||
//
|
||||
// Created by Sean Kim on 1/9/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum KEY {
|
||||
static let kakaoAppKey: String = "8ff66cbbf8fb74673db5c642c64dda73"
|
||||
static let appStoreURL: String = ""
|
||||
// public var SERVER_URL = "http://ipstein.myds.me:6004/"
|
||||
#if DEVELOPE
|
||||
static let apiLink: String = ""
|
||||
#else
|
||||
static let apiLink: String = "http://ipstein.myds.me:6000/"
|
||||
#endif
|
||||
|
||||
static let naverGeocodingURL: String = "https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode?"
|
||||
static let naverReverseGeocodingURL: String = "https://naveropenapi.apigw.ntruss.com/map-reversegeocode/v2/gc?"
|
||||
static let naverClientID = ("X-NCP-APIGW-API-KEY-ID", "qc2z3auwbq")
|
||||
static let naverClientSecret = ("X-NCP-APIGW-API-KEY", "OY9b0gsIppzwvqzYMcamyBGSI3QblFsICGBBuHpN")
|
||||
|
||||
// 주소 검색 web 주소
|
||||
static let githubPagesLink: String = "https://sean-59.github.io/Kakao-Postcode/"
|
||||
|
||||
}
|
102
JJUNGTABLE/Common/Prj_Common/Parameter.swift
Normal file
102
JJUNGTABLE/Common/Prj_Common/Parameter.swift
Normal file
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// Parameter.swift
|
||||
// RememberbyAnything
|
||||
//
|
||||
// Created by Sean Kim on 4/26/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
//MARK: - Alert 관련 데이터 정의
|
||||
|
||||
enum AlertType {
|
||||
case sign
|
||||
}
|
||||
|
||||
struct AlertData {
|
||||
var title: String
|
||||
var body: String
|
||||
var button: [ButtonType]
|
||||
|
||||
init(title: String = "알림", body: String, button: [ButtonType] = [.init(name: "확인", role: .none, function: nil)]) {
|
||||
self.title = title
|
||||
self.body = body
|
||||
self.button = button
|
||||
}
|
||||
}
|
||||
|
||||
struct ButtonType {
|
||||
var name: String
|
||||
var role: ButtonRole?
|
||||
var function: (()->())?
|
||||
}
|
||||
|
||||
// MARK: - API ERROR
|
||||
struct API_ERROR:Error {
|
||||
var caseType: CASE
|
||||
var message: String
|
||||
|
||||
enum CASE: Error{
|
||||
case API_PLIST_WRONG
|
||||
case API_PATH_WRONG
|
||||
case API_CONNECT
|
||||
case API_READ
|
||||
}
|
||||
}
|
||||
|
||||
struct LOGIN_ERROR: Error {
|
||||
var caseType: CASE
|
||||
var message: String
|
||||
|
||||
enum CASE: Error {
|
||||
case KAKAO_LOGIN
|
||||
case KAKAO_ACC_USER
|
||||
case APPLE_LOGIN
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - FB_CRUD 데이터 정의
|
||||
struct FB_ERROR:Error {
|
||||
var caseType: CASE
|
||||
var message: String
|
||||
|
||||
enum CASE: Error{
|
||||
case FB_CREATE_PARSING
|
||||
case FB_CREATE_SAVING
|
||||
case FB_UPDATE_DOC_READ
|
||||
case FB_UPDATE_TRY
|
||||
case FB_READ_PARSING
|
||||
case FB_READ_PATH
|
||||
case FB_READ_EXIST
|
||||
case FB_DELETE_TRY
|
||||
case FB_PATH_WRONG
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//MARK: - 유저 관련 데이터 정의
|
||||
enum Role: String {
|
||||
case Admin = "A"
|
||||
case Tester = "T"
|
||||
|
||||
case Master = "M"
|
||||
case Employee = "E"
|
||||
}
|
||||
|
||||
struct userData {
|
||||
var name: String
|
||||
var code: String
|
||||
var company: String
|
||||
var role: Role
|
||||
var noReadAlert: [String]
|
||||
var readAlert: [String]
|
||||
|
||||
init(name: String, code: String, company: String, role: Role, noReadAlert: [String], readAlert: [String]) {
|
||||
self.name = name
|
||||
self.code = code
|
||||
self.company = company
|
||||
self.role = role
|
||||
self.noReadAlert = noReadAlert
|
||||
self.readAlert = readAlert
|
||||
}
|
||||
}
|
84
JJUNGTABLE/Common/Prj_Common/UI_Func_Prefix.swift
Normal file
84
JJUNGTABLE/Common/Prj_Common/UI_Func_Prefix.swift
Normal file
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// UI_Func_Prefix.swift
|
||||
// SwiftDataTest
|
||||
//
|
||||
// Created by Sean Kim on 3/27/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum FontType {
|
||||
case Title
|
||||
case Content
|
||||
case Small
|
||||
}
|
||||
|
||||
enum Icon {
|
||||
static let left = Image(systemName: "chevron.left")
|
||||
static let right = Image(systemName: "chevron.right")
|
||||
static let up = Image(systemName: "chevron.up")
|
||||
static let down = Image(systemName: "chevron.down")
|
||||
|
||||
static let menu = Image(systemName: "line.3.horizontal")
|
||||
static let house = Image(systemName: "house")
|
||||
static let magazine = Image(systemName: "magazine")
|
||||
static let calendar = Image(systemName: "calendar")
|
||||
static let gearshape = Image(systemName: "gearshape")
|
||||
|
||||
static let plus = Image(systemName: "plus")
|
||||
static let bookmark = Image(systemName: "bookmark")
|
||||
static let bookmark_fill = Image(systemName: "bookmark.fill")
|
||||
|
||||
static let book = Image(systemName: "book")
|
||||
static let book_closed = Image(systemName: "book.closed")
|
||||
static let books = Image(systemName: "books.vertical")
|
||||
|
||||
static let filter = Image(systemName: "line.3.horizontal.decrease")
|
||||
|
||||
static let circle = Image(systemName: "circle")
|
||||
static let circle_fill = Image(systemName: "circle.fill")
|
||||
static let circle_check = Image(systemName: "checkmark.circle")
|
||||
static let smallCircle = Image(systemName: "smallcircle.filled.circle")
|
||||
|
||||
static let checkmark = Image(systemName: "checkmark")
|
||||
|
||||
|
||||
static let trash = Image(systemName: "trash")
|
||||
|
||||
static let xmarkCircle = Image(systemName: "xmark.circle.fill")
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - 고유 코드 생성
|
||||
enum CodeName: String{
|
||||
case group = "01"
|
||||
case item = "02"
|
||||
}
|
||||
|
||||
func makeCode(_ name: CodeName) -> String{
|
||||
let dateString = Date().convertString("yyyyMMddHHmmssSSS") // 17글자
|
||||
return "\(name.rawValue):\(dateString)"
|
||||
}
|
||||
|
||||
func generate8Code() -> String {
|
||||
let letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
let digits = "0123456789"
|
||||
let allCharacters = letters + digits
|
||||
var code: [Character] = []
|
||||
|
||||
for _ in 0..<3 {
|
||||
if let digit = digits.randomElement() {
|
||||
code.append(digit)
|
||||
}
|
||||
}
|
||||
for _ in 0..<5 {
|
||||
if let char = allCharacters.randomElement() {
|
||||
code.append(char)
|
||||
}
|
||||
}
|
||||
code.shuffle()
|
||||
|
||||
return String(code)
|
||||
}
|
47
JJUNGTABLE/Common/Prj_Common/ViewModel.swift
Normal file
47
JJUNGTABLE/Common/Prj_Common/ViewModel.swift
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// ViewModel.swift
|
||||
// CheckAnything
|
||||
//
|
||||
// Created by Sean Kim on 5/29/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
class ViewModel: ObservableObject {
|
||||
@UserDefault (key: "isUpdate", defaultValue: "N") var isUpdate
|
||||
|
||||
@UserDefault (key: "deviceToken", defaultValue: "") var deviceToken
|
||||
@UserDefault (key: "fcmToken", defaultValue: "") var pushFCMToken
|
||||
|
||||
@UserDefault (key: "notiBadge", defaultValue: 0) var notiBadge
|
||||
|
||||
|
||||
/*
|
||||
@UserDefault (key: "userId", defaultValue: "") var userId
|
||||
@UserDefault (key: "noReadAlertList", defaultValue: [""]) var noReadAlertList
|
||||
@UserDefault (key: "errorApp", defaultValue: [String:String]()) var errorApp
|
||||
*/
|
||||
|
||||
@Published var showAlert: Bool = false
|
||||
|
||||
var alertData: AlertData = .init(body: "")
|
||||
|
||||
// func setBadge() async {
|
||||
// let center = UNUserNotificationCenter.current()
|
||||
// do {
|
||||
// try await center.setBadgeCount(noReadAlertList.count)
|
||||
// } catch {
|
||||
// // Handle any errors.
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
func systemErrorAlert() -> AlertData {
|
||||
return .init(body: """
|
||||
시스템 문제가 발생하였습니다.
|
||||
확인 후 다시 실행해주세요.
|
||||
""",
|
||||
button: [ButtonType(name: "확인", role: .none , function: {exit(1)})])
|
||||
}
|
||||
}
|
50
JJUNGTABLE/Document.md
Normal file
50
JJUNGTABLE/Document.md
Normal file
|
@ -0,0 +1,50 @@
|
|||
# 계정 관련
|
||||
|
||||
## KAKAO
|
||||
Link:https://developers.kakao.com/console/app/987284
|
||||
Account: sean.kk@kakao.com
|
||||
|
||||
## NAVER
|
||||
Link: https://console.ncloud.com/naver-service/application
|
||||
Account: sean_kk@naver.com
|
||||
|
||||
# Protocol 관련
|
||||
## 공통단으로 사용하기 위한 protocol 운영시에 Any로 값을 보내는데 해당 값을 구성하는 방식은 보내는 쪽의 (Object's Identifier: Data) 식으로 보내서 구분이 가능하게 하는게 좋을것 같다.
|
||||
|
||||
|
||||
# 주석
|
||||
## [THINKING] 의 경우 조건 생각해볼것
|
||||
## Remove, test 처리 된 코드는 다 삭제 할 예정
|
||||
|
||||
|
||||
|
||||
func readDataBase(key: String, completion: @escaping (DataBase) -> Void) {
|
||||
// key 가 빈값으로 올 경우 바로 Type으로 만들지만 그게 아니고 key에 다른 주소가 같이 들어온다면 그 부분을 포함해서 주소 구성
|
||||
self.readDataBase(key: "go") { result in
|
||||
if let check2 = result as? DB_FAILURE {
|
||||
printLog(check2)
|
||||
}
|
||||
|
||||
}
|
||||
self.readDataBase(key: "") { result in
|
||||
printLog(result)
|
||||
}
|
||||
|
||||
|
||||
if key == "" {
|
||||
completion(DB_FAILURE(key: "out", type: .friends, errorType: .db_CreateERROR))
|
||||
} else {
|
||||
completion(DB_SUCCESS(key: "go", type: .getFriend))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DatabaseManager().readDataBase(.user, key: loginId) { dataBase in
|
||||
if let db = dataBase as? DB_SUCCESS {
|
||||
printLog(db.key)
|
||||
printLog(db.value)
|
||||
}
|
||||
else if let db = dataBase as? DB_FAILURE {
|
||||
printLog(db.key)
|
||||
}
|
||||
}
|
32
JJUNGTABLE/GoogleService-Info.plist
Normal file
32
JJUNGTABLE/GoogleService-Info.plist
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>API_KEY</key>
|
||||
<string>AIzaSyCeHcMO2hbmfxuYaiC8BATYvLbpWdQyNxA</string>
|
||||
<key>GCM_SENDER_ID</key>
|
||||
<string>480609810019</string>
|
||||
<key>PLIST_VERSION</key>
|
||||
<string>1</string>
|
||||
<key>BUNDLE_ID</key>
|
||||
<string>kr.kro.sean-k.JJUNGTABLE</string>
|
||||
<key>PROJECT_ID</key>
|
||||
<string>jjungtable-e5087</string>
|
||||
<key>STORAGE_BUCKET</key>
|
||||
<string>jjungtable-e5087.appspot.com</string>
|
||||
<key>IS_ADS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_ANALYTICS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_APPINVITE_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_GCM_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_SIGNIN_ENABLED</key>
|
||||
<true></true>
|
||||
<key>GOOGLE_APP_ID</key>
|
||||
<string>1:480609810019:ios:9a49754201d22353462a56</string>
|
||||
<key>DATABASE_URL</key>
|
||||
<string>https://jjungtable-e5087-default-rtdb.firebaseio.com</string>
|
||||
</dict>
|
||||
</plist>
|
82
JJUNGTABLE/Info.plist
Normal file
82
JJUNGTABLE/Info.plist
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>ipstein.myds.me</key>
|
||||
<dict>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSExceptionRequiresForwardSecrecy</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>ServerURL</key>
|
||||
<dict>
|
||||
<key>httpURL</key>
|
||||
<string>http://ipstein.myds.me</string>
|
||||
<key>httpsURL</key>
|
||||
<string>https://ipstein.myds.me</string>
|
||||
<key>PORT</key>
|
||||
<string>6000</string>
|
||||
</dict>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>kakao8ff66cbbf8fb74673db5c642c64dda73</string>
|
||||
|
||||
<string>app-1-480609810019-ios-9a49754201d22353462a56</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>FirebaseAppDelegateProxyEnabled</key>
|
||||
<false/>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>kakaokompassauth</string>
|
||||
<string>kakaolink</string>
|
||||
<string>kakaoplus</string>
|
||||
</array>
|
||||
<key>NMFClientId</key>
|
||||
<string>qc2z3auwbq</string>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>NPSfont_regular.otf</string>
|
||||
<string>NPSfont_bold.otf</string>
|
||||
<string>NPSfont_extrabold.otf</string>
|
||||
</array>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UISceneConfigurationName</key>
|
||||
<string>Default Configuration</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>external-accessory</string>
|
||||
<string>fetch</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
12
JJUNGTABLE/JJUNGTABLE.entitlements
Normal file
12
JJUNGTABLE/JJUNGTABLE.entitlements
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
<key>com.apple.developer.applesignin</key>
|
||||
<array>
|
||||
<string>Default</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
BIN
JJUNGTABLE/Resource/.DS_Store
vendored
Normal file
BIN
JJUNGTABLE/Resource/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
JJUNGTABLE/Resource/Fonts/NPSfont_bold.otf
Normal file
BIN
JJUNGTABLE/Resource/Fonts/NPSfont_bold.otf
Normal file
Binary file not shown.
BIN
JJUNGTABLE/Resource/Fonts/NPSfont_extrabold.otf
Normal file
BIN
JJUNGTABLE/Resource/Fonts/NPSfont_extrabold.otf
Normal file
Binary file not shown.
BIN
JJUNGTABLE/Resource/Fonts/NPSfont_regular.otf
Normal file
BIN
JJUNGTABLE/Resource/Fonts/NPSfont_regular.otf
Normal file
Binary file not shown.
BIN
JJUNGTABLE/Resource/Images/AppleIcon.PNG
Normal file
BIN
JJUNGTABLE/Resource/Images/AppleIcon.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
JJUNGTABLE/Resource/Images/JJUNGIcon.png
Normal file
BIN
JJUNGTABLE/Resource/Images/JJUNGIcon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 84 KiB |
BIN
JJUNGTABLE/Resource/Images/KakaoIcon.png
Normal file
BIN
JJUNGTABLE/Resource/Images/KakaoIcon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
BIN
JJUNGTABLE/Resource/Images/tableArchitecture.png
Normal file
BIN
JJUNGTABLE/Resource/Images/tableArchitecture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 82 KiB |
BIN
JJUNGTABLE/View/.DS_Store
vendored
Normal file
BIN
JJUNGTABLE/View/.DS_Store
vendored
Normal file
Binary file not shown.
134
JJUNGTABLE/View/ContentView.swift
Normal file
134
JJUNGTABLE/View/ContentView.swift
Normal file
|
@ -0,0 +1,134 @@
|
|||
//
|
||||
// ContentView.swift
|
||||
// JJUNGTABLE
|
||||
//
|
||||
// Created by Sean Kim on 3/5/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
|
||||
struct ContentView: View {
|
||||
@EnvironmentObject var viewModel: ViewModel
|
||||
|
||||
@StateObject private var login = Login()
|
||||
|
||||
@State private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
|
||||
@State private var selectedOption = "Option 1"
|
||||
let options = ["Option 1", "Option 2", "Option 3", "Option 4"]
|
||||
@State private var phoneNum = ("0000", "0000")
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
Spacer()
|
||||
Button {
|
||||
Login().tryKakaoLogin()
|
||||
.sink { completion in
|
||||
if case .failure(_) = completion {
|
||||
viewModel.alertData = .init(body: "SNS 로그인에 실패했습니다.")
|
||||
viewModel.showAlert.toggle()
|
||||
}
|
||||
} receiveValue: { data in
|
||||
printLog(data)
|
||||
API.shared.readData(path: "/db/userInfo/read",
|
||||
queryItems: [
|
||||
URLQueryItem(name: "id", value: data),
|
||||
URLQueryItem(name: "type", value: "01")
|
||||
])
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .finished:
|
||||
break
|
||||
case .failure(let error):
|
||||
printLog(error)
|
||||
}
|
||||
} receiveValue: { data in
|
||||
do {
|
||||
let decoder = JSONDecoder()
|
||||
let cid = try decoder.decode([UserCID].self, from: data)
|
||||
printLog(cid)
|
||||
} catch {
|
||||
printLog(error)
|
||||
self.viewModel.alertData = self.viewModel.systemErrorAlert()
|
||||
self.viewModel.showAlert.toggle()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
} label: {
|
||||
Text("Kakao ID로 로그인")
|
||||
.font(.nps(font: .bold, size: 32))
|
||||
}
|
||||
.padding(.bottom,20)
|
||||
|
||||
Button {
|
||||
login.tryAppleLogin()
|
||||
.sink { completion in
|
||||
|
||||
} receiveValue: { data in
|
||||
printLog("APPLE ID: \(data)")
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
} label: {
|
||||
Text("Apple ID로 로그인")
|
||||
.font(.nps(font: .bold, size: 32))
|
||||
}
|
||||
|
||||
|
||||
HStack(spacing: 0){
|
||||
Picker("Select an option", selection: $selectedOption) {
|
||||
ForEach(options, id: \.self) { option in
|
||||
Text(option).tag(option)
|
||||
}
|
||||
}
|
||||
.pickerStyle(MenuPickerStyle()) // This line makes the Picker a dropdown menu
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.foregroundStyle(.clear)
|
||||
.frame(height: 50)
|
||||
}
|
||||
|
||||
TextField("digit 4",text: $phoneNum.0)
|
||||
|
||||
TextField("last",text: $phoneNum.1)
|
||||
.font(.Content)
|
||||
.padding([.top,.bottom,.leading],5)
|
||||
.frame(height: 50)
|
||||
.cornerRadius(10)
|
||||
|
||||
}
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.stroke(Color.brand,lineWidth: 2)
|
||||
.foregroundStyle(.clear)
|
||||
}
|
||||
.padding()
|
||||
|
||||
|
||||
Button {
|
||||
login.sendCodeToPhone()
|
||||
} label: {
|
||||
Text("code")
|
||||
.font(.nps(font: .bold, size: 32))
|
||||
}
|
||||
|
||||
Button {
|
||||
login.verifyCode(code: phoneNum.1)
|
||||
} label: {
|
||||
Text("veri")
|
||||
.font(.nps(font: .bold, size: 32))
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
}
|
||||
.fullPage(.pageBack)
|
||||
.setBaseViewModifier()
|
||||
}
|
||||
}
|
236
README.md
Normal file
236
README.md
Normal file
|
@ -0,0 +1,236 @@
|
|||
README.md
|
||||
# 목차
|
||||
[1. 개발 일지](#개발-일지)
|
||||
[2. 생각 거리](#생각-거리)
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
# 개발 일지
|
||||
### 2024.02.27
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. 새롭게 작업 진행
|
||||
- 서류 작업 진행 중 : [Google Docs](https://docs.google.com/document/d/1g_SE7cnZK-gPrU62XBe27zmer31h1jtdHPL6NYA95OY/edit?usp=sharing)
|
||||
|
||||
</details>
|
||||
|
||||
### 2024.02.01
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. UI Code 전환
|
||||
- TopView 전환 완료
|
||||
- BottomView 전환 완료
|
||||
- CommonAlertVC 에 Coordinator 적용완료
|
||||
- 디테일한 부분은 전환 요소 파악해서 로직 자체를 다시 짤 필요가 있어 보임
|
||||
|
||||
</details>
|
||||
|
||||
### 2024.01.29
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. common 단 변화 시도 및 각종 VC들 변환 작업 중
|
||||
|
||||
</details>
|
||||
|
||||
### 2024.01.28
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. UI 작성 코드들 모듈화(?) 처리를 통해서 중복되는 코드 줄임
|
||||
- Common.TopView 변환 작업 중
|
||||
- MainVC 변환 작업 중
|
||||
|
||||
</details>
|
||||
|
||||
### 2024.01.26
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. Coordinator 패턴 적용 중
|
||||
- AppCoordinator 생성
|
||||
- IntroCoordinator 생성 - IntroVC 와 적용
|
||||
- LoginCoordinator 생성 - LoginVC 와 적용
|
||||
- MainCoordinator 생성 - MainVC 미적용
|
||||
2. .xib -> SnapKit Code 전환
|
||||
- IntroVC 에 대한 화면 UI 코드 전환 완료
|
||||
- LoginVC 에 대한 화면 UI 코드 전환 완료
|
||||
- MainVC 화면 UI 코드 전환 중
|
||||
3. VC 기능 동작 확인
|
||||
- IntroVC 패턴 적용하고 코드 전환 후 기능 적용 완료
|
||||
- LoginVC 패턴 적용하고 코드 전환 후 기능 적용 완료
|
||||
- MainVC 전환 적용 중
|
||||
|
||||
</details>
|
||||
|
||||
### 2024.01.25
|
||||
<details>git
|
||||
<summary>내용</summary>
|
||||
|
||||
1. 프로젝트 진행 방향에 대한 생각 변경
|
||||
- 최대한 빠르게 만들어지는 대로 만들어서 앱을 스토어에 올릴 생각이었으나 데이터를 관리하는 서버와 DB단의 변경으로 인해서 프로젝트 자체의 대규모 변경이 필요함을 느낌
|
||||
|
||||
2. 변경 내용
|
||||
1. iOS APP 관련
|
||||
- Design Pattern 도입 (Coordinator Pattern, Observer Pattern)
|
||||
- UI 작성 방식 변경 (xib -> code)
|
||||
- 서버가 추가 됨으로 인해 내부 로직들 대거 변경
|
||||
2. 서버 추가
|
||||
- firebase 의 realTime DB를 활용한 데이터 이용을 하였으나 실 서버에 대한 필요성을 느끼게 되어 서버를 추가 하기로 결정
|
||||
- 물리적인 서버는 개인 NAS가 존재하여 해당 기기를 활용
|
||||
- Back 단은 Node.js 를 사용
|
||||
- DB 는 MongoDB(MySQL) 를 사용
|
||||
3. MongoDB 에 테이블 생성
|
||||
<div align = center>
|
||||
<img src ="./JJUNGTABLE/Resource/Images/tableArchitecture.png" width=300>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
### 2024.01.22
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. 개발 무기한 중단
|
||||
- 서버 및 데이터 전환 작업
|
||||
- firebase -> node.js
|
||||
- firebase.realtiemDB -> MariaDB
|
||||
|
||||
</details>
|
||||
|
||||
### 2024.01.18
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. 예약 PAGE 수정
|
||||
- 뒤에 시간 건들고 앞에 건들면 뒤에꺼 날아가는 오류
|
||||
2. Main 자체에서 오류들 발생
|
||||
- 싹다 로직 전체 수정으로 해결
|
||||
|
||||
</details>
|
||||
|
||||
### 2024.01.17
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. 예약 PAGE 완료
|
||||
- 예약 관련 부분 완료
|
||||
2. Main 자체에서 오류들 발생
|
||||
- 문제
|
||||
1. 처음 가입시 이름이 Name으로 표기되는 오류
|
||||
2. 예약 받고 나면 떠있는 창 지우고 오늘의 일정에 해당되면 오늘의 일정에도 띄우는 작업을 진행해야 함
|
||||
- 당연 예약을 준사람도 작업을 해줘야 함
|
||||
3. 친구 추가 했을 떄 친구 요청을 갱신해야 받아오는데 이부분을 Main에서 하기에 해당 창 진입시 데이터 불러오게 변경
|
||||
4. 오늘의 일정에서 알림 토글 부분에 문제가 있음
|
||||
5. 친구cell에 있는 버튼 누르고 다른 VC 갔다가 돌아오면 친구cell 버튼이 동작을 안함
|
||||
- Main 자체에 Logic에서 문제점을 다수 발생하여 해당 부분을 전면 수정 중
|
||||
|
||||
</details>
|
||||
|
||||
### 2024.01.16
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. 예약 PAGE view 작동
|
||||
- DB 작업해서 다른 아이디에서도 아무 이상없이 잘 뜨는지 확인 필요
|
||||
2. Add Friends 부분에서 문제점이 발견
|
||||
- 친구 조회가 안되는 문제가 발견
|
||||
- 검색하는 DBManager 부분이 오류가 생긴듯
|
||||
|
||||
</details>
|
||||
|
||||
### 2024.01.13
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. SecondReserveView 추가
|
||||
- View 추가
|
||||
2. reserveData 추가해서 데이터 모델변경
|
||||
|
||||
</details>
|
||||
|
||||
### 2024.01.10
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. SearchAddressView 추가
|
||||
- 주소 입력하는데 Map을 보여줄 필요는 없다는 판단이 들어서 해당 부분을 Kakao의 주소 검색 API를 사용하기로 함
|
||||
- 따로 라이브러리로 존재하는것이 아니라 직접 HTML로 깃 허브에 올려둔 상태에서 Handler로 받아서 사용하는 WebView 형식으로 만들었음
|
||||
2. ReserveView 정리
|
||||
- 날짜 선택과 관련한 오류들 수정
|
||||
- 빈 값인데도 스크롤이 되는 오류 수정
|
||||
- 날짜, 시간, 장소 저장까지는 완료 이제 다음 예약 로직으로 넘어가면 됨
|
||||
|
||||
</details>
|
||||
|
||||
### 2024.01.09
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. View 다듬기
|
||||
- NaverView 와 ReserveView 가 조금더 자연스러울 수 있게 다듬기
|
||||
</details>
|
||||
|
||||
### 2024.01.07
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. MapView
|
||||
- 지도의 권한 허용 팝업 멘트 설정 및 권한 동작 구현
|
||||
- 현재 위치의 좌표를 받아와 현재 위치로 이동
|
||||
</details>
|
||||
|
||||
### 2024.01.06
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. ReserveView: datePicker 수정
|
||||
- 오늘 날짜보다 이전 예약은 막아야 하는데 내부 로직 상 날짜 입력이 제대로 처리 되지 않아 수정
|
||||
- yyyyMMdd 로 되어야 하는데 두자리 수 미만의 [월,일]이 입력되는 경우에 yyyyMd 와 같이 이상한 형태로 들어오는것을 확인 하였음
|
||||
2. ReserveView: MapView_searchView 수정
|
||||
- 지도 확인을 위해 띄우는 VC에서 AutoLayout 설정과 꺼졌다 켜짐 등에 대한 부분을 수정
|
||||
</details>
|
||||
|
||||
### 2024.01.05
|
||||
<details>
|
||||
<summary>내용</summary>
|
||||
|
||||
1. Git 등록
|
||||
- Local 관리를 하다가 체계적인 관리의 필요성을 다시 느껴서 작업물을 Git에 등록
|
||||
2. 내부 로직 수정
|
||||
- 내부 로직을 대대로 수정하면서 "InputUserInfoView" 와의 연결이 끊겨 있는 문제를 수정
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
# 생각 거리
|
||||
|
||||
### 생각중
|
||||
<details>
|
||||
<summary>2024.01.06</summary>
|
||||
|
||||
```
|
||||
[ ] 1. ViewDelegate와 BaseVCDelegate 를 활용하는데 이 부분을 나중에 다른 요소로 대체를 할 수 있으면 해야 할 것으로 보임
|
||||
```
|
||||
> 이건 너무 과하게 전 범위를 커버치려고 하다보니 세세하게 하나하나 다 고려를 해야 하고 값을 넣어줘야 하는 문제가 있음을 느낌
|
||||
|
||||
</details>
|
||||
|
||||
### 실행
|
||||
<details>
|
||||
<summary>2024.01.05</summary>
|
||||
|
||||
```
|
||||
[✓] 1. DatabaseManager의 Delegate 부분이 너무나도 불편하게 구성이 되어있음 completion 방식으로 변경 하는게 어떠할까 함
|
||||
```
|
||||
> 불편하게 구성이 되어있다보니 common 단으로 구성을 했음에도 불구하고 계속해서 특정 상황에 맞는 매개변수를 추가하고 하는 이상한 짓을 하게 되어서 수정을 해야 함을 느낌
|
||||
|
||||
</details>
|
Loading…
Reference in New Issue
Block a user