< 작업 환경 >
1. Model 파일
import Foundation
// 학생의 클래스 join을 위한 post api에서 사용
struct TryJoinClassM: Codable {
let status: StatusOfApiM
let result: ResultOfJoinClassM?
}
struct StatusOfApiM: Codable {
let status, message: String
}
struct ResultOfJoinClassM: Codable {
let studentId: Int
}
- 데이터 형태 예시 (참고)
{
"status": {
"status": "E000",
"message": "Success"
},
"result": {
"studentId": 93
}
}
2. Control or ViewModel 파일
import Foundation
// 클래스 합류 시도
class TryJoinClassVM: ObservableObject {
@Published var tryJoinClass: TryJoinClassM?
// test
private var test_url: URL?
private var test_data: Data?
init(api_url: String, parameters: [String: Any]) {
guard let url = URL(string: "\(api_url)/v2/student/join-class") else {
print("Invalid URL")
return
}
test_url = url
guard let postData = try? JSONSerialization.data(withJSONObject: parameters, options: []) else {
print("Failed to serialize parameters")
return
}
// test
test_data = postData
}
public func getData(completion: @escaping (TryJoinClassM?) -> Void) {
if let target_url = test_url {
var request = URLRequest(url: target_url)
request.httpMethod = "POST"
if let target_data = test_data {
request.httpBody = target_data
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in
guard error == nil else {
print("Error: \(error!)")
return
}
guard let data = data else {
print("No data received")
return
}
guard let jsonString = String(data: data, encoding: .utf8) else {
print("Error!")
return
}
print(jsonString)
do {
let responseDecoded = try JSONDecoder().decode(TryJoinClassM.self, from: data)
DispatchQueue.main.async {
self.tryJoinClass = responseDecoded
completion(self.tryJoinClass)
}
} catch {
print("\n\n[Error Decoding Response] TryJoinClassVM\n\n\(error.localizedDescription)\n\n\(error)")
}
})
.resume()
}
}
}
3. View 파일
import SwiftUI
struct TryJoinClassModalView: View {
// 모달 컨트롤
@Environment(\.presentationMode) var presentation
// 학급 합류 성공시 Main 들어갔을때 토스트 발생시키기 위한 변수
@AppStorage("isJoinClassNow") var isJoinClassNow : Bool = false
@AppStorage("isInClass") var isInClass : Bool = false
@AppStorage("classIdStored") var classIdStored: Int = 0
@AppStorage("userIdStored") var userIdStored : Int = 0
@AppStorage("studentIdStored") var studentIdStored: Int = 0
// 토스트 메시지
@State private var toast: FancyToast? = nil
// 초대 코드 입력받을 공간 만들어두기
@State private var inviteCode: String = ""
// 초대 코드 입력 전 상태 여부
@State private var isBeforeInputClassCode: Bool = true
// 초대 코드 정상 처리된 class id 정보 저장
@State private var classIdTryJoin: Int = 0
// 출석번호 저장
@State private var studentAttendanceNumberInput: String = ""
// 말풍선 안내
@State private var isShowingDescription = false
var body: some View {
VStack {
// 초대 코드 입력 전
if isBeforeInputClassCode {
Spacer()
Text("우리 반 초대코드 입력하기")
.font(.custom(pretendard_bold, size: 20))
.padding(.bottom, 50)
// 말풍선 시작
HStack {
Button(action: {
// 물음표 아이콘을 누를 때마다 설명 화면 표시 여부를 토글
withAnimation(.easeInOut) {
isShowingDescription.toggle()
}
// 2초 후에 설명 화면 숨김
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation(.easeInOut) {
isShowingDescription = false
}
}
}, label: {
HStack {
Text("초대코드는 무엇인가요?")
.font(.custom(pretendard_bold, size: 16))
.foregroundColor(Color.gray)
Image(systemName: "questionmark.circle")
.resizable()
.frame(width: 16, height: 16)
.foregroundColor(Color.gray)
}
})
.padding()
.overlay(
// 설명 화면
Group {
if isShowingDescription {
VStack(spacing: 0) {
ZStack {
// 뭉툭한 네모 모양의 테두리
RoundedRectangle(cornerRadius: 10)
.fill(Color.orange1)
.frame(width: 390, height:80)
.transition(.opacity)
// 설명 내용
Text("선생님께서 알려주신 코드를 입력해요. 우리반 초대 코드를 모르겠다면 선생님께 질문해봐요.")
.padding()
.multilineTextAlignment(.center)
.transition(.opacity)
.font(.custom(pretendard_bold, size: 16))
.foregroundColor(Color.orange2)
}
TriangleView()
.foregroundColor(Color.orange1)
.frame(width: 18, height: 12)
Rectangle()
.frame(width: 150, height: 120)
.foregroundColor(Color.white.opacity(0))
}
}
}
)
}
.padding(.top, 50)
// 말풍선 끝
TextField("초대코드 입력", text: $inviteCode)
.cornerRadius(8)
.padding()
.frame(width: 300)
.font(.custom(pretendard_bold, size: 16))
.background(Color.orange1)
Button {
if inviteCode != "" {
print("콜 시작")
@ObservedObject var classCodeCheck = ClassCodeVM(api_url: api_url, tryFindClassCode: inviteCode)
classCodeCheck.getData(completion: {data in
print("if let 진입")
if let classJoinInfo = classCodeCheck.findClass {
print("존재하는 학급인거 확인 완료")
if classJoinInfo.status.status == "E000" {
print("classJoinInfo E000 확인")
if let resultOfFindClass = classJoinInfo.result {
print("classJoinInfo.result if let 성공")
// 클래스 참가 신청 api VM 콜해야합니다!!! ToDoList
self.isBeforeInputClassCode = false
self.classIdTryJoin = resultOfFindClass.classId
}
} else {
print("if let 실패")
toast = FancyToast(type: .warning, title: "잘못된 초대코드", message: "존재하지 않는 초대코드이거나 초대코드에 오타가 있어요")
}
}
})
} else {
toast = FancyToast(type: .warning, title: "초대코드가 비어있어요", message: "초대코드를 입력해주세요")
}
} label: {
VStack {
// 초대 버튼 활성화
if inviteCode != "" {
ZStack {
RoundedRectangle(cornerRadius: 12)
.foregroundColor(Color.orange1)
.frame(width: 200, height: 50)
Text("우리반 가입하기")
.foregroundColor(Color.orange3)
.font(.custom(pretendard_bold, size: 16))
}
// 초대 버튼 비활성화
} else {
ZStack {
RoundedRectangle(cornerRadius: 12)
.foregroundColor(Color.gray1)
.frame(width: 200, height: 50)
Text("우리반 가입하기")
.foregroundColor(Color.gray3)
.font(.custom(pretendard_bold, size: 16))
}
}
}
}
// 초대코드로 클래스 찾기 종료
Spacer()
// 나가기 버튼
Button(action: {
presentation.wrappedValue.dismiss()
}) {
ZStack {
RoundedRectangle(cornerRadius: 12)
.foregroundColor(Color.gray150)
.frame(width: 125, height: 50)
Text("뒤로가기")
.foregroundColor(Color.black)
.font(.custom(pretendard_bold, size: 16))
}
}
// 초대 코드 입력 후 출석번호 입력
} else {
Spacer()
Text("내 출석번호 입력하기")
.font(.custom(pretendard_bold, size: 20))
.padding(.bottom, 20)
Text("출석번호를 모르겠다면 선생님께 여쭤보세요")
.font(.custom(pretendard_bold, size: 16))
.foregroundColor(Color.gray)
.padding(.bottom, 50)
TextField("내 출석번호를 입력해요", text: $studentAttendanceNumberInput)
.cornerRadius(8)
.padding()
.frame(width: 300)
.font(.custom(pretendard_bold, size: 16))
.background(Color.orange1)
Button {
if studentAttendanceNumberInput != "" {
@ObservedObject var joinClass = TryJoinClassVM(api_url: api_url, parameters: ["userId": userIdStored, "classId": classIdTryJoin, "studentNumber": studentAttendanceNumberInput])
joinClass.getData(completion: {data in
if let tryJoinClassButton = joinClass.tryJoinClass {
// 학급 합류 성공
if tryJoinClassButton.status.status == "E000" {
// 학생 아이디 저장
if let studentInfo = tryJoinClassButton.result {
self.studentIdStored = studentInfo.studentId
}
// 이외 클래스 진입 상태로 변경
self.isInClass = true
self.classIdStored = classIdTryJoin
self.isJoinClassNow = true
print("학급 가입 성공")
presentation.wrappedValue.dismiss()
} else if tryJoinClassButton.status.status == "E601" {
print("이미 학급에 합류되어있음")
toast = FancyToast(type: .error, title: "이미 학급에 합류해있어요", message: "내가 속한 학급 목록에서 우리 반을 찾아 들어가보세요.")
} else if tryJoinClassButton.status.status == "E606" {
print("이미 다른 친구 사용 출석번호")
toast = FancyToast(type: .error, title: "본인의 출석번호를 입력했나요?", message: "이미 다른 친구가 등록한 출석번호예요. 내 출석번호로 다른 친구가 가입했다면 선생님께 가서 이야기해봐요.")
} else {
print("시스템 에러")
toast = FancyToast(type: .error, title: "시스템 오류", message: "개발자에게 문의해주세요.")
}
} else {
print("tryJoinClassButton if let 실패")
}
})
} else {
toast = FancyToast(type: .warning, title: "출석 번호를 입력해봐요", message: "본인의 출석 번호를 입력해보세요. 모르겠다면 선생님께 여쭤봐도 좋아요.")
}
} label: {
VStack {
// 학급 Join 버튼 활성화
if studentAttendanceNumberInput != "" {
ZStack {
RoundedRectangle(cornerRadius: 12)
.foregroundColor(Color.orange1)
.frame(width: 200, height: 50)
Text("우리반 가입하기")
.foregroundColor(Color.orange3)
.font(.custom(pretendard_bold, size: 16))
}
// 초대 버튼 비활성화
} else {
ZStack {
RoundedRectangle(cornerRadius: 12)
.foregroundColor(Color.gray1)
.frame(width: 200, height: 50)
Text("우리반 가입하기")
.foregroundColor(Color.gray3)
.font(.custom(pretendard_bold, size: 16))
}
}
}
}
// 초대코드로 클래스 찾기 종료
Spacer()
// 나가기 버튼
Button(action: {
presentation.wrappedValue.dismiss()
}) {
ZStack {
RoundedRectangle(cornerRadius: 12)
.foregroundColor(Color.gray150)
.frame(width: 185, height: 50)
Text("내 학급 목록 보러가기")
.foregroundColor(Color.black)
.font(.custom(pretendard_bold, size: 16))
}
}
}
}
.toastView(toast: $toast)
}
}
'Development > iOS' 카테고리의 다른 글
[Objective-C] 입문하기 - 02 : Hello World를 출력하는 앱 만들기 (0) | 2023.06.14 |
---|---|
[Objective-C] 입문하기 - 01 : 기본적인 코드 짜서 메소드 만들고 활용해 콘솔에 의도대로 출력해보기 (0) | 2023.06.09 |
[SwiftUI] Api 통신 연결 소스코드 예시 - Method : GET (0) | 2023.05.16 |
[SwiftUI][NavigationStack] NavigationLink & List를 활용한 + NavigationLink & ForEach를 활용한 정보 목록 화면 및 상세 정보 화면 구현 방법 (0) | 2023.04.28 |
[SwiftUI] 로딩바 출력하는 기능 예제 (0) | 2023.04.28 |