2022. 8. 28. 17:19ใProgramming/Swift
https://hello-developer.tistory.com/52
https://hello-developer.tistory.com/53?category=951601
https://velog.io/@leesangsu200/Firestore์์-๋ฐ์ดํฐ-๊ฐ์ ธ์ค๊ธฐ
https://github.com/DeveloperAcademy-POSTECH/MC3-Team3-Puhaha
https://firebase.google.com/docs/firestore/query-data/queries
- error handling
db.collection("users").addDocument(data: [:]) { error in
// this code is run after the operation is complete
if error == nil {
// operation success
} else {
// error, do something
}
}
- db์ data๋ฅผ ๋ฃ๋ ํจ์ (write)
func setUserName(userIdentifier: String, userName: String) {
db.collection("Users").document(userIdentifier).updateData(["name": userName])
}
func setDefaultUserData(userIdentifier: String) {
db.collection("Users").document(userIdentifier).setData(["familyCode": "",
"name": "",
"pokeState": ["pokedBy": "",
"pokedtime": ""],
"pokingTool": ["color": "",
"tool": ""]])
}
.setData() : ์ด๊ธฐ์ data๋ฅผ set ํ ๋ ์ฌ์ฉ (๋ฎ์ด์ฐ๊ธฐ ์ฃผ์)
.updateData() : ๊ธฐ์กด ํ๋์ ๊ฐ์ update ํ ๋ ์ฌ์ฉ
db.collection("users").document("documentPath").delete()
-> ํด๋น path์ ๋ฌธ์๊ฐ ํต์ผ๋ก ์ง์์ง
db.collection("users").document("documentPath").updateData(["name": FieldValue.delete()])
-> ํน์ ํ๋์ ๊ฐ๋ง ์ง์์ง
- db์์ data๋ฅผ ๊ฐ์ ธ์ค๋ ํจ์ (read)
func getSignInUser(userIdentifier: String, completion: @escaping () -> Void) {
loginedUser = User(accountId: userIdentifier)
db.collection("Users").document(userIdentifier).addSnapshotListener { [self] (document, error) in
if let document = document {
let name = document.data()?["name"] as? String ?? ""
let familyCodes = document.data()?["familyCode"] as? [String] ?? []
let pokingTool = document.data()?["pokingTool"] as? [String: String] ?? [:]
let pokeStateValue = document.data()?["pokeState"] as? [String: String] ?? [:]
let pokingToolColor: String = pokingTool["color"]?.lowercased() ?? ""
let pokingToolType: String = pokingTool["tool"]?.lowercased() ?? ""
let toolImage: UIImage = UIImage(named: "\(pokingToolColor)_\(pokingToolType)") ?? UIImage()
let pokedBy: String = pokeStateValue["pokedBy"] ?? ""
let pokedTime: String = pokeStateValue["pokedTime"] ?? ""
loginedUser.setName(name: name)
loginedUser.setToolImage(toolImage: toolImage)
loginedUser.setToolType(with: pokingToolType)
loginedUser.setToolColor(with: pokingToolColor)
loginedUser.setFamilyCodes(code: familyCodes)
loginedUser.setPoke(poke: Poke(pokedBy: pokedBy, pokedTime: pokedTime))
completion()
} else {
print(error ?? "")
}
}
}
- escaping closure๋ก ๊ฐ์ ๊ฐ์ ธ์ค๋ ๊ฒฝ์ฐ
: ๋จ์ํ ์๋ฒ์ data๋ง ๊ฐ์ ธ์ค๋ ๊ฒฝ์ฐ์๋ getDocument๋ฅผ ํ์ฉํ๋ฉด ๋๋ค.
-> but, UI๋ฅผ ๊ทธ๋ฆฌ๋ ๊ณผ์ ์์ data๊ฐ ๋ถ๋ฌ์จ ๋ค, ํ๋ฉด์ ๊ทธ๋ ค์ผ ํ๋ ๊ฒฝ์ฐ์๋
completion handler๋ฅผ ํ์ฉํ์ฌ ์ฒ๋ฆฌํ๋ค.
1) ํน์ document ๋ฅผ ์กฐํํ๊ธฐ (pull)
db.collection("users").document("documentPath").getDocument { (snapshot, error) in
if error == nil && snapshot != nil && snapshot!.data() != nil {
print(snapshot!.data()
}
}
: collection ๋ด๋ถ์ ํน์ document ์์ getDocument
2) collection ๋ด๋ถ์ ๋ชจ๋ document ์กฐํํ๊ธฐ (pull)
db.collection("users").getDocuments { (snapshot, error) in
if error == nil && snapshot != nil {
for document in snapshot!.documents {
print(document.documentID)
}
} else {
// error. do something
}
}
: collection์์ getDocuments
3) ํน์ document ๋ฅผ ์กฐํํ๊ธฐ (push)
pull: ์๋๋ db์ data๋ฅผ ๊ทธ๋ฅ ๋ฐ์์ด -> push: database ์์ ๋ณํ๊ฐ ์๊ฒผ์ ๋, data๋ฅผ ๋ณด๋ด์ฃผ๋ ๊ฒ
let listener = db.collection("users").document("documentPath").addSnapshotListener { (snapshot, error) in
if error == nil && snapshot != nil && snapshot!.data() != nil {
print(snapshot!.data())
}
}
: ์๋ฒ์์ ๋ณํํ๋ data๋ฅผ ์์์ผํ ๋ ์ฌ์ฉ (addSnapshotListener ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํ์ฌ, ์ ๋ฐ์ดํธ ์ฌํญ์ ์์ )
4) collection ๋ด๋ถ์ ๋ชจ๋ documents ์กฐํ (push)
let listener = db.collection("users").addSnapshotListener { (snapshot, error) in
if error == nil && snapshot != nil {
// ๋ณํ๊ฐ ์๋๊ฒ๋ง ๊ฐ์ ธ์ฌ ์ ์๋ค.
for change in snapshot!.documentChanges {
print(change.document.documentID)
}
} else {
// error. do something
}
}
์ถ์ฒ: https://hello-developer.tistory.com/53?category=951601
: ๋ชจ๋ ๋ฌธ์๋ฅผ ๋์ง ์๊ณ , ๋ณํ๊ฐ ์๊ธด ๊ฐ๋ง ๊ฐ์ ธ์ฌ ๋ change ํ์ฉ
- Firestore Queries
: whereField๋ฅผ ํตํด ์ํ๋ ์กฐ๊ฑด์ data๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค!
: google firebase ๊ณต์๋ฌธ์ ์ฐธ์กฐ https://firebase.google.com/docs/firestore/query-data/queries#compound_queries
let query = db.collection("users").whereField("age", isEqualTo: 30)
query.getDocuments { (snapshot, error) in
let docs = snapshot!.documents
for doc in docs {
print(doc.documentID)
}
}
// Create a reference to the cities collection
let citiesRef = db.collection("cities")
// Create a query against the collection.
let query = citiesRef.whereField("state", isEqualTo: "CA")
let capitalCities = db.collection("cities").whereField("capital", isEqualTo: true)
- 1) ์ฃผ๊ฐ "CA"์ธ ๋์๋ค์ ๋ฐํ, 2) ์๋์ธ ๋์(capital์ด true์ธ)๋ค์ ๋ฐํ
db.collection("cities").whereField("capital", isEqualTo: true).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
}
}
}
-> whereField ๊น์ง๊ฐ ์ฟผ๋ฆฌ ๊ฐ์ฒด ์์ฑ -> get ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
- array ์ฟผ๋ฆฌ
1) in
let citiesRef = db.collection("cities")
citiesRef.whereField("country", in: ["USA", "Japan"])
-> country ํ๋ ๊ฐ์ด USA ์ด๊ฑฐ๋ Japan์ธ ๋ชจ๋ ๋ฌธ์๋ฅผ ๋ฐํํ๋ค. (ex) ์ํ๋์์ค์ฝ, ๋ก์ค์์ ค๋ ์ค, ํ ๊ต,
2) not-in
citiesRef.whereField("country", notIn: ["USA", "Japan"])
-> country ํ๋๊ฐ ์กด์ฌํ๋, USA or Japan์ ์๋๋ฉฐ, ๋ํ ๋น ๊ฐ๋ ์๋์ด์ผ ํจ!
: ๊ทธ๋ฐ ๋ชจ๋ cities collection์ ๋ฌธ์๋ฅผ ๋ฐํํ๋ค.
: ์ฃผ์์ฌํญ ์์ผ๋ ๊ณต์๋ฌธ์์์ ๋ ์ฝ์ด๋ณด์.
not-in ์ฟผ๋ฆฌ๋ ์ง์ ๋ ํ๋๊ฐ ์๋ ๋ฌธ์๋ฅผ ์ ์ธํฉ๋๋ค. ๋น ๋ฌธ์์ด(""), null, NaN(์ซ์ ์๋)์ ํฌํจํ๋ ์ด๋ค ๊ฐ์ผ๋ก๋ ์ค์ ๋๋ฉด ํ๋๊ฐ ์กด์ฌํฉ๋๋ค. x != null์ undefined๋ก ํ๊ฐ๋ฉ๋๋ค. null์ ๋น๊ต ๊ฐ ์ค ํ๋๋ก ๊ฐ์ง๊ณ ์๋ not-in ์ฟผ๋ฆฌ๋ ์ด๋ค ๋ฌธ์์๋ ์ผ์นํ์ง ์์ต๋๋ค.
3) array-contains-any
let citiesRef = db.collection("cities")
citiesRef.whereField("regions", arrayContainsAny: ["west_coast", "east_coast"])
- regions ํ๋๋ west_coast ๋๋ east_coast๊ฐ ํฌํจ๋ ๋ฐฐ์ด
- ์ค๋ณต๋๋ array-contains-any์ ๊ฒฐ๊ณผ๊ฐ ์ญ์
- array-contains-any: ํญ์ ๋ฐฐ์ด ๋ฐ์ดํฐ ์ ํ ๊ธฐ์ค์ผ๋ก ํํฐ๋ง: ๋ฐฐ์ด ๋์ regions ํ๋๊ฐ west_coast ๋ฌธ์์ด์ธ city ๋ฌธ์๋ X
: ๋จ์ํ ๊ฐ ๋ง์ ๋น๊ต๋ In์ ์ฌ์ฉํ๊ณ , ๋ฐฐ์ด ๋ด๋ถ์ ๋ฐ์ดํฐ์ ๋ํ ์ ๊ทผ์ array-contains-any ์ฌ์ฉ!
- ๋ณตํฉ ์ฟผ๋ฆฌ
: ์ฌ๋ฌ ๋ฑํธ ์ฐ์ฐ์๋ฅผ ํตํด, ๊ตฌ์ฒด์ ์ธ ์กฐ๊ฑด์ ๊ฑธ ์ ์๋ค. -> ๋ณตํฉ ์ฟผ๋ฆฌ๋ฅผ ๋ง๋ค ์ ์์
citiesRef
.whereField("state", isEqualTo: "CO")
.whereField("name", isEqualTo: "Denver")
citiesRef
.whereField("state", isEqualTo: "CA")
.whereField("population", isLessThan: 1000000)
ํ๋ 1๊ฐ์์๋ง ๋ฒ์ ํํฐ ์ฌ์ฉํ๋ ๊ฒ์ด ์ฌ๋ฐ๋ฅธ ์ฌ์ฉ! (๋ฒ์ ํํฐ๋ -> isLessThan ์ด๋ฐ ์ฐ์ฐ! ๋ถ๋ฑํธ ์ฐ์ฐ๋ค!)
citiesRef
.whereField("state", isGreaterThanOrEqualTo: "CA")
.whereField("population", isGreaterThan: 1000000)
์๋ชป๋ ์ฌ์ฉ! ์ฌ๋ฌ ํ๋์ ๋ฒ์ ํํฐ ์ฌ์ฉ (ํํฐ: , ๋ค์ ๋ถ๋ ๊ฒ์ผ๋ก ์ดํด)
[ MC2 project์์ ํ์ฉํ ์์ ]
// User.swift
import UIKit
class User {
private let accountId: String?
private var name: String?
private var loginForm: Int?
private var toolImage: UIImage?
private var toolType: String?
private var toolColor: String?
private var familyCodes: [String?]
private var pokeState: Poke?
init() {
self.accountId = nil
self.name = nil
self.loginForm = 0
self.toolImage = nil
self.toolType = nil
self.toolColor = nil
self.familyCodes = []
self.pokeState = nil
}
init(accountId: String) {
self.accountId = accountId
self.name = nil
self.loginForm = 0
self.toolImage = nil
self.toolType = nil
self.toolColor = nil
self.familyCodes = []
self.pokeState = nil
}
init(accountId: String, name: String) {
self.accountId = accountId
self.name = name
self.loginForm = 0
self.toolImage = nil
self.toolType = nil
self.toolColor = nil
self.familyCodes = []
self.pokeState = nil
}
init(accountId: String, name: String, toolImage: UIImage, toolType: String, toolColor: String, familyCodes: [String], pokeState: Poke) {
self.accountId = accountId
self.name = name
self.loginForm = 0
self.toolImage = toolImage
self.toolType = toolType
self.toolColor = toolColor
self.familyCodes = familyCodes
self.pokeState = pokeState
}
public func setName(name: String) {
self.name = name
}
public func getName() -> String {
return name ?? ""
}
public func setToolImage(toolImage: UIImage) {
self.toolImage = toolImage
}
public func getToolImage() -> UIImage {
return toolImage ?? UIImage()
}
public func setToolType(with tool: String) {
self.toolType = tool
}
public func getToolType() -> String {
return toolType ?? ""
}
public func setToolColor(with color: String) {
self.toolColor = color
}
public func getToolColor() -> String {
return toolColor ?? ""
}
public func setPoke(poke: Poke) {
self.pokeState = poke
}
}
// FirestoreManager.swift
import UIKit
import FirebaseFirestore
import FirebaseFirestoreSwift
class FirestoreManager: ObservableObject {
private let tagColor: [UIColor] = [.customPurple, .customBlue, .customGreen]
private var db = Firestore.firestore()
@Published var meals: [Meal]
@Published var user: User
@Published var loginedUser: User
@Published var userIdentifiers: [String]
@Published var families: [Family]
@Published var documentsCount = 0
@Published var isExistFamily: Bool
init() {
self.meals = []
self.user = User()
self.loginedUser = User()
self.userIdentifiers = []
self.families = [Family(user: User(accountId: "",
name: "๋ชจ๋",
toolImage: UIImage(named: "IconEveryoneFilter")!,
toolType: "",
toolColor: "",
familyCodes: [String()],
pokeState: Poke()),
isSelected: true)]
self.isExistFamily = false
}
func fetchMeals(familyCode: String, date: Date?, completion: @escaping () -> Void) {
var documentPath: Query = db.collection("Families").document(familyCode).collection("Meals")
if date != nil {
documentPath = documentPath.whereField("uploadDate", isEqualTo: date!.dateText)
}
documentPath.order(by: "uploadTime", descending: true).addSnapshotListener { [self] (querySnapshot, _) in
guard let documents = querySnapshot?.documents else {
print("No Documents")
return
}
meals = documents.map { (queryDocumentSnapshot) -> Meal in
let data = queryDocumentSnapshot.data()
let mealImageIndex = data["mealImageIndex"] as? String ?? ""
let tagsString = data["tags"] as? [String: String] ?? [:]
var tags: [Tag] = []
for key in tagsString.keys {
tags.append(Tag(content: tagsString[key] ?? "", backgroundColor: self.tagColor[Int(key) ?? 0]))
}
let userIdentifier = data["uploadUser"] as? String ?? ""
let uploadedTime = data["uploadTime"] as? String ?? ""
let uploadedDate = data["uploadDate"] as? String ?? ""
let reactionsValue = data["reactions"] as? [[String: String]] ?? []
var reactions: [Reaction?] = [nil]
for i in 0..<reactionsValue.count {
for key in reactionsValue[i].keys {
reactions.append(Reaction(reactionEmojiString: reactionsValue[i][key]!, reactedUserName: key))
}
}
let meal = Meal(mealImage: UIImage(), mealImageName: mealImageIndex, uploadUser: "", userIdentifier: userIdentifier, userIcon: UIImage(), tags: tags, uploadedDate: uploadedDate, uploadedTime: uploadedTime, reactions: reactions)
DispatchQueue.main.async {
completion()
}
return meal
}
}
}
func getUploadUser(userIdentifier: String, completion: @escaping () -> Void) {
user = User(accountId: userIdentifier)
db.collection("Users").document(userIdentifier).addSnapshotListener { [self] (document, error) in
if let document = document {
let name = document.data()?["name"] as? String ?? ""
let familyCodes = document.data()?["familyCode"] as? [String] ?? []
let pokingTool = document.data()?["pokingTool"] as? [String: String] ?? [:]
let pokeStateValue = document.data()?["pokeState"] as? [String: String] ?? [:]
let pokingToolColor: String = pokingTool["color"]?.lowercased() ?? ""
let pokingToolTool: String = pokingTool["tool"]?.lowercased() ?? ""
let toolImage: UIImage = UIImage(named: "\(pokingToolColor.lowercased())_\(pokingToolTool)") ?? UIImage()
/* Tool enum ์ฌ์ฉํ ๊ฒฝ์ฐ
let toolType = convertStringToToolType(string: pokingTool["tool"] ?? "")
let toolColor = convertStringToToolColor(string: pokingTool["color"] ?? "")
let toolImage: UIImage = UIImage(named: "\(toolColor)_\(toolType.imageFileName)") ?? UIImage()
*/
let pokedBy: String = pokeStateValue["pokedBy"] ?? ""
let pokedTime: String = pokeStateValue["pokedTime"] ?? ""
user.setName(name: name)
user.setToolImage(toolImage: toolImage)
user.setToolType(with: pokingToolTool)
user.setToolColor(with: pokingToolColor)
user.setFamilyCodes(code: familyCodes)
user.setPoke(poke: Poke(pokedBy: pokedBy, pokedTime: pokedTime))
completion()
} else {
print(error ?? "")
}
}
}
func getSignInUser(userIdentifier: String, completion: @escaping () -> Void) {
loginedUser = User(accountId: userIdentifier)
db.collection("Users").document(userIdentifier).addSnapshotListener { [self] (document, error) in
if let document = document {
let name = document.data()?["name"] as? String ?? ""
let familyCodes = document.data()?["familyCode"] as? [String] ?? []
let pokingTool = document.data()?["pokingTool"] as? [String: String] ?? [:]
let pokeStateValue = document.data()?["pokeState"] as? [String: String] ?? [:]
let pokingToolColor: String = pokingTool["color"]?.lowercased() ?? ""
let pokingToolType: String = pokingTool["tool"]?.lowercased() ?? ""
let toolImage: UIImage = UIImage(named: "\(pokingToolColor)_\(pokingToolType)") ?? UIImage()
let pokedBy: String = pokeStateValue["pokedBy"] ?? ""
let pokedTime: String = pokeStateValue["pokedTime"] ?? ""
loginedUser.setName(name: name)
loginedUser.setToolImage(toolImage: toolImage)
loginedUser.setToolType(with: pokingToolType)
loginedUser.setToolColor(with: pokingToolColor)
loginedUser.setFamilyCodes(code: familyCodes)
loginedUser.setPoke(poke: Poke(pokedBy: pokedBy, pokedTime: pokedTime))
completion()
} else {
print(error ?? "")
}
}
}
func getFamilyMember(familyCode: String, completion: @escaping () -> Void) {
db.collection("Families").document(familyCode).addSnapshotListener { [self] (document, error) in
if let document = document {
let userIdentifier: [String] = document.data()?["users"] as? [String] ?? []
userIdentifiers = userIdentifier
db.collection("Users").whereField("familyCode", isEqualTo: familyCode).addSnapshotListener { [self] (querySnapshot, _) in/*.getDocuments(source: .default) { [self] (querySnapshot, error) in*/
if let error = error {
print("Error getting documents: \(error)")
} else {
families = [Family(user: User(accountId: "",
name: "๋ชจ๋",
toolImage: UIImage(named: "IconEveryoneFilter")!,
toolType: "",
toolColor: "",
familyCodes: [String()],
pokeState: Poke()),
isSelected: true)]
for document in querySnapshot!.documents {
let data = document.data()
let accountId = data["accountId"] as? String ?? ""
let name = data["name"] as? String ?? ""
/* ์๋๋ก์ด๋ ๊ฐ๋ฐ์ด ๋๋ ๊ฒฝ์ฐ 0: ์ ํ ๊ณ์ ๋ก๊ทธ์ธ, 1: ๊ตฌ๊ธ ๊ณ์ ๋ก๊ทธ์ธ์ผ๋ก ์ฌ์ฉ ์์
let loginForm = data["loginForm"] as? Int ?? 0
*/
let familyCodes = data["familyCode"] as? [String] ?? []
let pokingTool = data["pokingTool"] as? [String: String] ?? [:]
let pokeStateValue = data["pokeState"] as? [String: String] ?? [:]
let pokingToolColor: String = pokingTool["color"]?.lowercased() ?? ""
let pokingToolTool: String = pokingTool["tool"]?.lowercased() ?? ""
let toolImage: UIImage = UIImage(named: "\(pokingToolColor)_\(pokingToolTool)") ?? UIImage()
let pokedBy: String = pokeStateValue["pokedBy"] ?? ""
let pokedTime: String = pokeStateValue["pokedTime"] ?? ""
let user = User(accountId: accountId,
name: name,
toolImage: toolImage,
toolType: pokingToolTool,
toolColor: pokingToolColor,
familyCodes: familyCodes,
pokeState: Poke(pokedBy: pokedBy, pokedTime: pokedTime))
families.append(Family(user: user, isSelected: false))
}
completion()
}
}
} else {
print(error ?? "")
}
}
}
func setPokingToolData(userIdentifier: String, tool passedTool: PokeTool) {
let tool = passedTool.tool.imageFileName
let color = convertUIColorToString(color: passedTool.color)
db.collection("Users").document(userIdentifier).updateData([
"pokingTool.tool": tool,
"pokingTool.color": color
])
}
func addReaction(familyCode: String, meal: Meal, newReaction: [String: String]) {
db.collection("Families").document(familyCode).collection("Meals").whereField("uploadUser", isEqualTo: meal.userIdentifier).whereField("uploadDate", isEqualTo: meal.uploadedDate).whereField("uploadTime", isEqualTo: meal.uploadedTime).getDocuments { querySnapshot, error in
if let error = error {
print("Error getting documents: \(error)")
} else {
for document in querySnapshot!.documents {
let data = document.data()
var reactions = data["reactions"] as? [[String: String]] ?? []
reactions.append(newReaction)
document.reference.updateData(["reactions": reactions])
}
}
}
}
func setFamilyCode(userIdentifier: String, code: String) {
db.collection("Users").document(userIdentifier).updateData(["familyCodes": [code]])
}
// TODO: ํน์ ์ฝ๋๋ฅผ ์ง์ฐ๊ธฐ ์ํด parameter๋ก code๋ฅผ ๋ฐ์์ผ ํ๋?
func deleteFamilyCode(userIdentifier: String) {
db.collection("Users").document(userIdentifier).updateData(["familyCodes": ""])
}
func setUserName(userIdentifier: String, userName: String) {
db.collection("Users").document(userIdentifier).updateData(["name": userName])
}
func setDefaultUserData(userIdentifier: String) {
db.collection("Users").document(userIdentifier).setData(["familyCode": "",
"name": "",
"pokeState": ["pokedBy": "",
"pokedtime": ""],
"pokingTool": ["color": "",
"tool": ""]])
}
func isExistFamily(roomCode: String, completion: @escaping () -> Void) {
db.collection("Families").document(roomCode).getDocument { document, error in
let isExist = document?.exists ?? false
self.isExistFamily = isExist
completion()
}
}
func setUpMeals(image: UIImage,
userIdentifier: String,
familyCode: String,
tags: [String]) {
let today = Date.now
let documentRef = db.collection("Families").document(familyCode).collection("Meals")
let storageManager = StorageManager()
let imageName: String = "img_\(today.dateText)_\(today.timeNumberText)\(today.secondsText()).jpeg"
storageManager.uploadMealImage(image: image, familyCode: familyCode, imageName: imageName) {
documentRef.addDocument(data: [
"mealImageIndex": imageName,
"uploadUser": userIdentifier,
"uploadDate": Date().dateText,
"uploadTime": Date().timeNumberText,
"tags": ["0": tags[0],
"1": tags[1],
"2": tags[2]
],
"reactions": []])
}
print("์ด๋ฏธ์ง์ด๋ฆ: \(imageName)")
}
'Programming > Swift' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Swift] CollectionView Cell _ Lingo Feedback (0) | 2022.11.01 |
---|---|
[Swift] ๋ค๋ฅธ file (viewController) ๊ฐ์ ์กฐ์ (0) | 2022.08.31 |
โฟ iOS Swift Closure & @escaping ํด๋ก์ ์ ๋ฆฌ (0) | 2022.08.26 |
๐ Fileprivate ๋? (0) | 2022.07.21 |
๐ฉโ๐ป UUID(Universally Unique IDentifier)๋? (0) | 2022.06.30 |