Carthage — isang desentralisadong tagapamahala ng dependency para sa mga proyektong Cocoa (iOS, macOS, watchOS, tvOS), na nagbu-build ng mga binary framework mula sa source code. Hindi tulad ng CocoaPods, hindi awtomatikong binabago ng Carthage ang proyekto — ang developer mismo ang nagdaragdag ng mga binuong framework sa Xcode. Ang Carthage ay isinulat sa Swift, gumagamit ng Cartfile para ilarawan ang mga dependency at sumusuporta sa parallel na pagbuo. Ayon sa datos ng GitHub repository, ang Carthage ay nakakolekta ng higit sa 15,000 bituin at nananatiling isang niche ngunit hinahanap na tool para sa mga proyektong nangangailangan ng minimal na interbensyon sa konfigurasyon ng Xcode.
Mga pangunahing punto
carthage bootstrap o carthage update — si-clone ng Carthage ang mga repository at kino-compile ang mga ito sa .xcframeworkCarthage — isang tagapamahala ng dependency na may desentralisadong arkitektura, nilikha noong 2014 ng mga developer mula sa komunidad ng Swift. Hindi gumagamit ang Carthage ng sentral na rehistro ng mga spesipikasyon — bawat library ay direktang kinokonekta mula sa Git repository sa pamamagitan ng URL o pangalan sa GitHub. Dina-download ng Carthage ang source code, binu-build ito sa isang binary framework (.xcframework o .framework) at nagbibigay sa developer ng handa nang artepakto para sa manu-manong integrasyon sa Xcode project.
Ang arkitektura ng Carthage ay may tatlong bahagi: ang CLI tool na carthage, ang file ng konfigurasyon na Cartfile at ang direktoryo na Carthage/Build/ na may mga binuong framework. Ang pangunahing pagkakaiba ng Carthage sa CocoaPods — ang kawalan ng awtomatikong pagbabago ng .xcodeproj. Hindi gumagawa ang Carthage ng .xcworkspace, hindi kino-konfigura ang mga flag ng compiler at hindi gumagawa ng Pods.xcconfig. Ang developer mismo ang nagdaragdag ng mga framework sa proyekto sa pamamagitan ng Xcode, na nagbibigay ng buong kontrol sa proseso ng integrasyon.
Carthage ay gumagamit ng parallel na pagbuo ng mga dependency, na makabuluhang nagpapabilis ng proseso sa mga multi-core na processor. Bawat dependency ay binu-build bilang isang hiwalay na target, at awtomatikong nire-resolve ng Carthage ang graph ng mga transitive dependency, binu-build ang mga ito sa tamang pagkakasunod-sunod. Ayon sa mga benchmark ng komunidad, ang Carthage ay nagbu-build ng 15–20 dependency sa average na 30–60 segundo sa mga modernong Mac, na mas mabilis kaysa sa CocoaPods para sa mga proyektong may maraming library. Sinusuportahan ng Carthage ang lahat ng platform ng Apple: iOS, macOS, watchOS at tvOS, at mula sa bersyon 0.38+ — pagbuo ng universal na .xcframework para sa suporta ng mga simulator at Apple Silicon device.
Carthage ay nag-c-clone ng Git repository ng bawat dependency, nag-switch sa tinukoy na bersyon (tag, commit o branch) at nagpapatakbo ng xcodebuild para buuin ang framework. Awtomatikong tinutukoy ng Carthage ang uri ng Xcode project (framework, dynamic framework, static library) batay sa build scheme. Kung ang proyekto ay naglalaman ng maraming scheme, ginagamit ng Carthage ang default na scheme (una sa alphabetical order). Pagkatapos ng pagbuo, kino-kopya ng Carthage ang tapos na framework sa Carthage/Build/ at gumagawa ng file na Cartfile.resolved na may pagtatakda ng eksaktong bersyon. Sinusuportahan ng Carthage ang caching ng mga binuong framework — hindi isinasagawa ang muling pagbuo kung walang pagbabago sa mga dependency.
Ang mga transitive dependency sa Carthage ay pinangangasiwaan sa pamamagitan ng Cartfile.resolved: gumagawa ang Carthage ng graph ng lahat ng kinakailangang dependency at binu-build ang mga ito sa tamang pagkakasunod-sunod. Kung dalawang library ang nakadepende sa parehong third-party library, isang beses itong binu-build ng Carthage at ginagamit para sa pareho. Nag-aabiso ang Carthage ng mga error sa pagbuo na may pagtukoy ng partikular na target at dahilan — pinapadali nito ang pag-diagnose ng mga problema.
Cartfile — isang file ng konfigurasyon sa wikang Ruby (format ng Cartfile) na nagde-define ng mga dependency ng Carthage project. Ang Cartfile ay matatagpuan sa root ng proyekto sa tabi ng .xcodeproj. Bawat linya ng Cartfile ay naglalarawan ng isang dependency: pinagmulan (Git-URL, GitHub repository) at bersyon. Sinusuportahan ng syntax ang pagtatakda ng bersyon sa pamamagitan ng mga tag, commit at branch.
# Mga pangunahing dependency ng Carthage
github "Alamofire/Alamofire" ~> 5.9
github "SnapKit/SnapKit" ~> 5.7
github "onevcat/Kingfisher" == 8.0.0Ang direktiba na github "Owner/Repo" — pinaikling form para sa mga GitHub repository. Awtomatikong gumagawa ang Carthage ng URL sa anyong https://github.com/Owner/Repo.git. Para sa GitLab, Bitbucket at iba pang Git host, ginagamit ang buong URL: git "https://gitlab.com/owner/repo.git". Mga operator ng bersyon: ~> 5.9 (anumang bersyon mula 5.9 hanggang 6.0 hindi kasama ang 6.0), == 8.0.0 (eksaktong bersyon), >= 1.0 (minimal na bersyon). Ang pagkonekta ng isang partikular na commit ay maaaring gawin sa pamamagitan ng github "owner/repo" "abc1234".
Carthage ay sumusuporta sa maraming direktoryo para sa iba't ibang konfigurasyon: Cartfile(pangunahing), Cartfile.private (para sa panloob na mga dependency na hindi nai-publish) at Cartfile.resolved (awtomatikong nabubuo). Ang mga pribadong dependency ay kapaki-pakinabang para sa mga library na ginagamit lamang sa Development build, halimbawa mga testing framework.
# Cartfile — mga pangunahing dependency
github "Alamofire/Alamofire" ~> 5.9
github "SwiftyJSON/SwiftyJSON" ~> 4.0
github "realm/realm-swift" ~> 10.0
# Buong URL para sa GitLab
git "https://gitlab.com/company/internal-lib.git" == 2.1.1
# Branch ng pag-develop
github "marmelroy/PhoneNumberKit" "development"github at git — dalawang uri ng pinagmulan sa Cartfile. Ang una ay para lamang sa GitHub at awtomatikong bumubuo ng URL. Ang pangalawa — para sa anumang pampubliko o pribadong Git repository na may buong URL. Ang bersyon ay maaaring tukuyin sa pamamagitan ng tag (== 2.1.1), semantic range (~> 5.9), pangalan ng branch ("development") o hash ng commit ("a1b2c3d"). Ang paggamit ng semantic ranges (~>) ay inirerekomenda para sa mga dependency na sumusunod sa SemVer — pinoprotektahan nito laban sa mga breaking change sa pag-update.
Cartfile.resolved ay awtomatikong nabubuo pagkatapos ng carthage update. Itinatakda nito ang eksaktong bersyon ng lahat ng naka-install na dependency, kasama ang mga transitive. Ang file ay dapat itago sa Git — kung wala ito, ang utos na carthage bootstrap sa ibang machine ay bubuo ng mga library ayon sa parehong patakaran, ngunit maaaring mag-iba ang mga bersyon. Ang carthage outdated ay nagpapakita ng listahan ng mga lumang dependency kung saan may bagong bersyon.
Carthage ay ini-install sa pamamagitan ng Homebrew — ang karaniwang tagapamahala ng package para sa macOS. Mga alternatibong paraan: pag-install mula sa handa nang .pkg installer mula sa GitHub o pagbuo mula sa source code. Ang Carthage ay nangangailangan ng Xcode na may Command Line Tools (kasama ang xcodebuild), at sa Apple Silicon Mac — Rosetta 2 para sa ilang lumang dependency.
# Pag-install ng Carthage gamit ang Homebrew
brew install carthage
# Pagsusuri ng bersyon
carthage version
# Pag-install mula sa .pkg (kung Homebrew ay hindi available)
# I-download ang Carthage.pkg mula sa GitHub Releases at i-install nang manu-manoPagkatapos ng pag-install, ang inisyalisasyon ng Carthage project ay nagsisimula sa paggawa ng Cartfile sa root ng proyekto. Ang Carthage ay walang utos na init — ang file ay ginagawa nang manu-mano sa text editor. Pagkatapos punan ang Cartfile ng mga dependency, pinapatakbo ng developer ang carthage bootstrap (kung mayroon nang Cartfile.resolved) o carthage update (unang pag-install o pag-update). Si-clone ng Carthage ang mga repository, binu-build ang mga framework at inilalagay ang mga ito sa Carthage/Build/.
Pag-update ng Carthage ay ginagawa sa pamamagitan ng brew upgrade carthage. Ang bersyon ay sinusuri gamit ang utos na carthage version. Ang huling stable na bersyon noong mid-2025 — 0.40 na may default na suporta para sa .xcframework, pinahusay na parallel na pagbuo at buong suporta para sa Swift 6. Simula sa bersyon 0.39, huminto ang Carthage sa pagbuo ng mga lumang .framework nang walang compatibility bridge — inirerekomenda na eksplicitong tukuyin ang --use-xcframeworks.
# Pag-update ng Carthage gamit ang Homebrew
brew upgrade carthage
# Pag-install ng tiyak na bersyon
brew install carthage@0.39
# Buong muling pag-install
brew uninstall carthage && brew install carthageTandaan: Hindi gumagawa ang Carthage ng .xcworkspace at hindi binabago ang .xcodeproj. Hindi tulad ng CocoaPods, iniiwan ng Carthage ang buong kontrol sa konfigurasyon ng Xcode sa developer. Ibig sabihin nito na pagkatapos i-install ang mga dependency, kailangan manu-manong idagdag ang mga framework sa Xcode (tingnan ang seksyong «Integrasyon ng Carthage framework sa Xcode»). Kinakailangan din ng Carthage na ang bawat dependency ay naglalaman ng Xcode project o workspace na may framework target — kung hindi, magtatapos ang pagbuo sa error.
Carthage ay nag-aalok ng tatlong pangunahing utos para sa pagtatrabaho sa mga dependency: bootstrap, update at build. Ang carthage bootstrap ay binu-build ang mga dependency mula sa umiiral na Cartfile.resolved — inirerekomenda para sa mga CI environment at developer na sumasali sa proyekto. Ang carthage update ay ina-update ang Cartfile.resolved sa pinakabagong mga bersyon (isinasaalang-alang ang mga limitasyon ng Cartfile) at isinasagawa ang pagbuo. Ang carthage build ay binu-build ang lahat ng tinukoy na dependency nang hindi nagse-save ng mga bersyon.
# Unang pag-install (ina-update ang mga bersyon)
carthage update --use-xcframeworks --platform iOS
# Muling pagbuo batay sa nakapirming bersyon
carthage bootstrap --use-xcframeworks --platform iOS
# Pagbuo ng isang dependency lamang
carthage build Alamofire --platform iOSAng flag na --use-xcframeworks ay nag-uutos sa Carthage na magbuo ng universal na .xcframework sa halip na mga lumang .framework. Tinitiyak nito ang suporta para sa parehong simulator at tunay na device, pati na rin ang Apple Silicon Mac nang walang karagdagang script. Ang flag na --platform iOS ay naglilimita sa pagbuo sa isang platform ng iOS — ito ay makabuluhang nagpapabilis ng proseso, lalo na kung ang proyekto ay naglalaman ng mga cross-platform na library.
Carthage ay sumusuporta sa parallel na pagbuo sa pamamagitan ng flag na --cache-builds, na nag-ca-cache ng mga nai-build nang framework. Sa muling pagbuo, sinusuri ng Carthage ang hash ng Git commit at, kung hindi nagbago ang code, lalaktawan ang kompilasyon. Para sa mga CI server, inirerekomenda na i-cache ang direktoryo na Carthage/Build/ at ~/Library/Caches/carthage/. Sinusuportahan din ng Carthage ang --verbose para sa detalyadong logging at --no-use-binaries para sa sapilitang pagbuo mula sa source code (kung hindi nagtitiwala ang developer sa mga pre-built binary).
| Utos | Aksyon |
|---|---|
carthage update | Ina-update ang Cartfile.resolved at binu-build ang lahat ng framework |
carthage bootstrap | Binu-build ang framework batay sa umiiral na Cartfile.resolved nang walang pag-update |
carthage build | Binu-build ang tinukoy na dependency nang hindi nagtatakda ng bersyon |
carthage outdated | Nagpapakita ng listahan ng mga dependency na may available na update |
carthage checkout | Si-clone lamang ang mga repository nang walang pagbuo |
Integrasyon ng Carthage framework sa Xcode ay ginagawa nang manu-mano sa apat na hakbang. Pagkatapos isagawa ang carthage update o bootstrap, lahat ng binuong framework ay nasa Carthage/Build/iOS/ (o kaukulang platform). Binubuksan ng developer ang Xcode project, pinipili ang target ng application at idinadagdag ang mga framework sa General → Frameworks, Libraries, and Embedded Content. Para sa mga runtime framework (dynamic library), dapat piliin ang «Embed & Sign» — kung hindi, babagsak ang app sa paglunsad na may error na «dyld: Library not loaded».
Carthage para sa static na library ay mas simple — hindi sila nangangailangan ng embed phase, dahil direktang nagli-link sa executable file ng application. Gayunpaman, ang Carthage ay default na binu-build ang dynamic na framework (maliban sa mga explicit na naka-konfigurang static library). Para sa mga proyekto kung saan mahalaga ang pag-minimize ng laki ng application, inirerekomenda ang paggamit ng static linking sa pamamagitan ng Xcode settings.
Karagdagang hakbang — pagdaragdag ng Input Files sa Build Phase → Run Script. Ang Carthage ay nangangailangan ng script para sa pag-alis ng mga artepakto ng simulator mula sa binuong framework (strip simulator architectures). Ang script na ito ay kinakailangan para sa mga App Store build:
# Run Script para sa App Store (strip simulator architectures)
FRAMEWORKS_DIR="${SRCROOT}/Carthage/Build/iOS"
for framework in "$FRAMEWORKS_DIR"/*.framework; do
bash "$BUILD_DIR/src/scripts/strip-framework.sh" "$framework"
doneCarthage ay hindi nangangailangan ng paggamit ng .xcworkspace — lahat ng dependency ay nai-build na sa mga binary framework. Ang Carthage ay direktang gumagana sa .xcodeproj, hindi tulad ng CocoaPods na gumagawa ng workspace. Pinapadali nito ang kontrol ng bersyon at konfigurasyon ng CI, dahil hindi binabago ng Carthage dependency ang konfigurasyon ng Xcode project. Ang tanging pagbabago — pagdaragdag ng mga framework sa target, na naitala sa .pbxproj.
| Hakbang | Aksyon |
|---|---|
| 1 | Patakbuhin ang carthage update --use-xcframeworks |
| 2 | I-drag ang mga framework mula sa Carthage/Build/ patungo sa General → Frameworks |
| 3 | Itakda ang Embed & Sign para sa dynamic na framework |
| 4 | Magdagdag ng Run Script Phase para sa pag-alis ng simulator architecture |
| 5 | Buuin ang proyekto — dapat awtomatikong mag-link ang mga framework |
Carthage, CocoaPods at Swift Package Manager (SPM) — ang tatlong pangunahing tagapamahala ng dependency sa pag-develop ng iOS. Ang Carthage ay nakikilala sa pamamagitan ng desentralisadong diskarte, ang CocoaPods ay nag-aalok ng sentralisadong rehistro, ang SPM — naka-embed na solusyon mula sa Apple. Ang pagpili sa pagitan ng mga ito ay depende sa mga kinakailangan ng proyekto, laki ng koponan at kinakailangang antas ng automation.
| Kriterya | Carthage | CocoaPods | SPM |
|---|---|---|---|
| Arkitektura | Desentralisado | Sentralisadong rehistro | Naka-embed sa Xcode |
| Wika ng konfigurasyon | Cartfile (parang Ruby) | Podfile (Ruby DSL) | Package.swift (Swift) |
| Integrasyon sa Xcode | Manu-mano (drag & drop) | Sa pamamagitan ng workspace | Naka-embed |
| Transitive dependency | Awtomatiko | Awtomatiko | Awtomatiko |
| Rehistro ng library | Wala (Git repository) | 100,000+ sa Specs | ~65,000 |
| Suporta sa resource | Wala | Oo (resource bundles) | Oo (Resources) |
| Bilis ng pagbuo | Mabilis (parallel) | Katamtaman | Mabilis |
| Kontrol sa integrasyon | Buong | Awtomatiko | Awtomatiko |
Carthage ay pinipili para sa mga proyektong nangangailangan ng minimal na interbensyon sa konfigurasyon ng Xcode at buong kontrol sa proseso ng integrasyon. Ang Carthage ay mainam para sa mga open library at framework, kung saan nais ng may-akda na bigyan ang mga gumagamit ng kakayahang magbuo ng mga dependency nang nakapag-iisa. Ang Carthage ay sikat din sa komunidad ng mga developer na pinahahalagahan ang pilosopiya ng UNIX: bawat tool ay gumagawa ng isang bagay nang maayos. Ang CocoaPods ay nananatiling pamantayan para sa mga corporate project na may dose-dosenang dependency kung saan mahalaga ang automation. Ang SPM — pagpipilian para sa mga bagong proyekto, dahil naka-embed ito sa Xcode at aktibong binuo ng Apple.
Migrasyon sa pagitan ng mga tagapamahala ay nangangailangan ng iba't ibang diskarte. Carthage → SPM: tanggalin ang mga framework mula sa Xcode, tanggalin ang Cartfile at idagdag ang Package Dependencies sa pamamagitan ng File → Add Package Dependencies. Carthage → CocoaPods: tanggalin ang mga Carthage framework, gumawa ng Podfile, idagdag ang mga dependency at patakbuhin ang pod init && pod install. Sa migrasyon mula Carthage patungo sa CocoaPods o SPM, nawawala ang pangangailangan na manu-manong i-update ang mga framework — lahat ng dependency ay ina-update sa isang utos. Ang Carthage ay nananatiling may kaugnayan para sa mga proyekto kung saan mahalaga ang pag-iwas sa vendor lock-in at pagpapanatili ng transparency ng pagbuo ng dependency.
Carthage — isang matatag na tool, ngunit ang mga developer ay paminsan-minsang nakakaranas ng mga karaniwang problema, lalo na sa pagbuo sa mga CI server, pag-update ng Xcode o pagbabago ng mga bersyon ng Swift. Karamihan sa mga problema ay nalulutas sa pamamagitan ng pag-clear ng cache, tamang konfigurasyon ng --use-xcframeworks at pagsusuri ng pagiging tugma ng minimal na bersyon ng iOS.
Error «The file manager returned an error» — nangyayari kapag nasira ang cache ng Carthage o conflict sa mga karapatan ng file. Solusyon: tanggalin ang cache gamit ang utos na rm -rf ~/Library/Caches/carthage at muling patakbuhin ang carthage bootstrap. Tumutulong din ang pagtanggal ng direktoryo na Carthage/ sa proyekto at muling pagbuo. Sa mga CI server, ang cache ng Carthage ay dapat i-update lamang kapag nagbago ang Cartfile.resolved.
Error «No such module» — hindi natagpuan ang framework sa Xcode, kahit na matagumpay ang pagbuo ng Carthage. Solusyon: suriin ang path ng framework sa General → Frameworks, Libraries, and Embedded Content. Ang framework ay dapat nasa Carthage/Build/iOS/. Tiyakin na ang .xcframework ay naidagdag nang tama (i-drag muli). Para sa dynamic na framework, suriin ang Embed & Sign. Kung magpapatuloy ang error — idagdag ang FRAMEWORK_SEARCH_PATHS sa Build Settings.
Error sa pagbuo dahil sa hindi pagkakatugma ng Swift — ang library ay binuo para sa ibang bersyon ng Swift kaysa sa proyekto. Solusyon: gamitin ang carthage update --no-use-binaries para sa sapilitang pagbuo mula sa source code na may parehong bersyon ng Swift. Kung hindi nagko-compile ang library sa kasalukuyang bersyon — gumamit ng .xcconfig para tukuyin ang bersyon ng Swift o i-fork ang library. Simula sa Carthage 0.39, ang --use-xcframeworks ay awtomatikong nagsasama ng tamang bersyon ng Swift sa binary.
Mga problema sa CI build — ang Carthage sa CI ay nangangailangan ng tamang konfigurasyon ng caching. Solusyon: i-cache ang Carthage/Build/ at ~/Library/Caches/carthage/. Gamitin ang carthage bootstrap --use-xcframeworks --platform iOS sa halip na update sa CI upang hindi baguhin ang mga bersyon. Para sa GitHub Actions, available ang opisyal na Carthage action. Para sa Jenkins — plugin na CarthageBuild. Ang Carthage ay maaaring bumagsak sa macOS na walang GUI — solusyon: i-install ang brew install xcode-build-server o idagdag ang key na -UseModernBuildSystem=NO.
| Problema | Sanhi | Solusyon |
|---|---|---|
| File manager error | Sirang cache | Linisin ang ~/Library/Caches/carthage/ |
| No such module | Hindi naidagdag ang framework sa Xcode | Suriin ang Framework sa target |
| Hindi pagkakatugma ng Swift | Iba't ibang bersyon ng Swift | --no-use-binaries o bagong bersyon ng Carthage |
| Error sa CI | Walang cache o GUI | I-konfigura ang cache ng Carthage/Build/ |
| Hindi binuo ang library | Walang Xcode project sa library | Suriin ang istraktura ng repository |
Mga madalas itanong
Carthage — isang desentralisadong tagapamahala ng dependency para sa mga platform ng Apple. Hindi tulad ng CocoaPods, hindi gumagamit ang Carthage ng sentral na rehistro ng library, hindi awtomatikong binabago ang Xcode project at hindi gumagawa ng .xcworkspace. Binu-build ng Carthage ang mga dependency sa mga binary framework na manu-manong idinadagdag ng developer sa Xcode. Ang CocoaPods, sa kabilang banda, ay nag-automate ng buong proseso sa pamamagitan ng Podfile.
Carthage ay ini-install sa pamamagitan ng Homebrew: brew install carthage. Bilang alternatibo — i-download ang Carthage.pkg mula sa GitHub Releases o buuin mula sa source code. Pagkatapos ng pag-install suriin ang bersyon: carthage version. Ang Carthage ay nangangailangan ng Xcode na may Command Line Tools. Sa Apple Silicon Mac ay maaaring kailanganin din ang Rosetta 2.
Cartfile — file ng konfigurasyon na isinulat ng developer: naglalaman ito ng mga pangalan ng library at operator ng bersyon (~> 5.9, == 8.0.0, pangalan ng branch). Cartfile.resolved ay awtomatikong nabubuo sa carthage update at nagtatakda ng eksaktong bersyon ng lahat ng naka-install na dependency. Ang Cartfile.resolved ay dapat itago sa Git — ginagarantiyahan nito ang reproducibility ng pagbuo sa lahat ng machine.
Carthage ay nangangailangan na ang library ay naglalaman ng tamang Xcode project o workspace na may framework target. Suriin kung accessible ang repository (hindi pribado nang walang key), kung tinukoy ang tamang bersyon (umiiral ang tag o commit), at sinusuportahan ng library ang iyong bersyon ng Xcode. Gamitin ang carthage build --verbose para sa detalyadong diagnosis. Kung ang library ay walang framework target, hindi ito mabubuo ng Carthage.
Carthage ay nananatiling may kaugnayan para sa mga proyektong nangangailangan ng desentralisadong pamamahala ng dependency, buong kontrol sa integrasyon at minimal na interbensyon sa Xcode project. Gayunpaman, karamihan sa mga bagong proyekto ay pumipili ng Swift Package Manager (SPM) — naka-embed ito sa Xcode, hindi nangangailangan ng karagdagang pag-install at aktibong binuo ng Apple. Ang Carthage ay inirerekomenda para sa mga legacy project kung saan mayroon nang pipeline ng pagbuo, o para sa mga library na ang mga may-akda ay nais bigyan ang mga gumagamit ng kalayaan na pumili ng paraan ng integrasyon.
Buod
brew install carthage, at pagbuo ng dependency sa pamamagitan ng carthage bootstrap o carthage update--no-use-binaries at konfigurasyon ng CI cachingGagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din