Skip to content
This repository has been archived by the owner on Jan 30, 2025. It is now read-only.

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
myawesomehub committed Sep 10, 2021
0 parents commit aa8993e
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
Original file line number Diff line number Diff line change
@@ -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>
31 changes: 31 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "PickEmoji",
platforms: [
.iOS(.v14)
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "PickEmoji",
targets: ["PickEmoji"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "PickEmoji",
dependencies: []),
.testTarget(
name: "PickEmojiTests",
dependencies: ["PickEmoji"]),
]
)
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# PickEmoji

A description of this package.
182 changes: 182 additions & 0 deletions Sources/PickEmoji/PickEmoji.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import SwiftUI

public struct PickEmoji: View {
@ObservedObject var mainViewModel: MainViewModel = MainViewModel()
@State public var numberOfEmojiPerRow: Int
public var completionHandler: ((String) -> Void)

public init(numberOfEmojiPerRow: Int, completionHandler: @escaping (String) -> Void) {
self.numberOfEmojiPerRow = numberOfEmojiPerRow
self.completionHandler = completionHandler
}

public var body: some View {
VStack(alignment: .leading) {
HStack {
Image(systemName: "magnifyingglass")
.foregroundColor(.black)
TextField("Search Emoji", text: $mainViewModel.emojiSearch)
.foregroundColor(.black)
}
.padding(.vertical)
.padding(.horizontal)
.background(Color.init(hex: "#EFF1F4"))
.cornerRadius(10)
.padding(.vertical, 16)

LazyVGrid(columns: Array(repeating: GridItem(), count: numberOfEmojiPerRow), spacing: 20) {
ForEach(mainViewModel.emojis, id: \.id) { item in
Text(item.emoji)
.onTapGesture {
completionHandler(item.emoji)
}
}
}

}
.padding(20)
.background(Color.init(hex: "#FAFAFA"))
.alert(isPresented: $mainViewModel.showError) {
Alert(title: Text(mainViewModel.errorTitle), message: Text(mainViewModel.errorMessage), dismissButton: .cancel())
}

}
}

struct Response: Decodable {
var results: [Emoji]
}

struct Emoji: Decodable, Hashable {
var id = UUID()
var emoji: String

private enum CodingKeys: String, CodingKey {
case emoji
}
}

class EmojiServiceManager {

enum NetworkError: Error {
case emojiNotFound
case timeOut
case wrongURL
}

enum NetworkMethods: String {
case GET = "GET"
case POST = "POST"
}

static func makeEmojiApiCall (
for query: String,
limit: Int,
completionHandler: @escaping (Result<Response, NetworkError>) -> ()
) {
let url = "https://api.emojisworld.fr/v1/search?q=\(query)&limit=\(limit)"
guard let safeURL = URL(string: "\(url)") else {
completionHandler(.failure(NetworkError.wrongURL))
return
}

var request = URLRequest(url: safeURL)
request.httpMethod = NetworkMethods.GET.rawValue

let session = URLSession(configuration: .default)

session.dataTask(with: request) { data, response, error in
guard let data = data else {
completionHandler(.failure(NetworkError.timeOut))
return
}

do {
let decoder = JSONDecoder()
let decodedData = try decoder.decode(Response.self, from: data)
completionHandler(.success(decodedData))
} catch {
completionHandler(.failure(NetworkError.emojiNotFound))
}
}.resume()
}

}

class MainViewModel: ObservableObject {

@Published var emojiSearch: String = "" {
didSet {
fetchEmoji()
}
}

@Published var emojis: [Emoji] = []

@Published var showError = false
@Published var errorTitle: String = "Make Sure to Connect With Internate"
@Published var errorMessage: String = ""

init() {
self.fetchEmoji()
}

private func fetchEmoji() {
let searchText = self.emojiSearch.isEmpty ? "smile" : self.emojiSearch
EmojiServiceManager.makeEmojiApiCall(for: searchText, limit: 100) { result in
switch result {
case .success(let data):
DispatchQueue.main.async {
self.emojis = data.results
}
case .failure(let error):
switch error {
case .emojiNotFound:
DispatchQueue.main.async {
self.errorMessage = "\(error.localizedDescription.description)"
self.showError = true
}
case .timeOut:
DispatchQueue.main.async {
self.errorMessage = "\(error.localizedDescription.description)"
self.showError = true
}
case .wrongURL:
DispatchQueue.main.async {
self.errorMessage = "\(error.localizedDescription.description)"
self.showError = true
}
}
}
}
}
}

extension Color {
init(hex: String) {
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int: UInt64 = 0
Scanner(string: hex).scanHexInt64(&int)
let a, r, g, b: UInt64
switch hex.count {
case 3: // RGB (12-bit)
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
case 6: // RGB (24-bit)
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
case 8: // ARGB (32-bit)
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
(a, r, g, b) = (1, 1, 1, 0)
}

self.init(
.sRGB,
red: Double(r) / 255,
green: Double(g) / 255,
blue: Double(b) / 255,
opacity: Double(a) / 255
)
}
}


11 changes: 11 additions & 0 deletions Tests/PickEmojiTests/PickEmojiTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import XCTest
@testable import PickEmoji

final class PickEmojiTests: XCTestCase {
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct
// results.
XCTAssertEqual(PickEmoji().text, "Hello, World!")
}
}

0 comments on commit aa8993e

Please sign in to comment.