DEV Community

linzhongxue
linzhongxue

Posted on

Full Analysis of "TasteLog" Food Community App Development Based on HarmonyOS Next

Full Analysis of "TasteLog" Food Community App Development Based on HarmonyOS Next

This article guides you through building a full-featured food social application "TasteLog" using HarmonyOS 5.0's ArkTS language and AppGallery Connect services. You'll master integration of core functionalities including user authentication, data storage, and location services.


1. Project Overview & Tech Stack

Application Scenario: Users share food exploration notes, bookmark favorite restaurants, and discover nearby food hotspots.

Core Technologies:

  • HarmonyOS 5.0 (Next API 10)
  • ArkTS Declarative Development
  • AGC Services: Auth, CloudDB, Cloud Storage, Cloud Functions
  • Location Services, Media Library Access

2. Project Setup & AGC Configuration

  1. Create HarmonyOS Project
// Initialize at entry/src/main/ets/pages/Index.ets
@Entry
@Component
struct Index {
  build() {
    Column() {
      Text('TasteLog')
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
    }
    .width('100%')
    .height('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. Enable AGC Services
  2. In AGC console enable:
    • Authentication (Phone/Email login)
    • CloudDB (Food data storage)
    • Cloud Storage (Image caching)
    • Cloud Functions (Business logic)

3. Core Function Implementation

1. User Authentication Module

// utils/AGCManager.ets
import agconnect from '@hw-agconnect/api';
import '@hw-agconnect/auth';

// Initialize AGC authentication
export function initAGC() {
  agconnect.instance().init();
}

// Phone login
export async function phoneLogin(phone: string, code: string): Promise<boolean> {
  try {
    const credential = agconnect.auth.PhoneAuthProvider.credentialWithVerifyCode(phone, code);
    await agconnect.auth().signIn(credential);
    return true;
  } catch (err) {
    console.error("Login failed: " + JSON.stringify(err));
    return false;
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Food Data Storage (CloudDB)

// model/FoodNote.ets
@ObjectType
export class FoodNote {
  @PrimaryKey()
  id: number = 0; // Primary key

  @Field()
  title: string = ''; // Note title

  @Field()
  restaurant: string = ''; // Restaurant name

  @Field()
  content: string = ''; // Detailed review

  @Field()
  userId: string = ''; // User ID

  @Field()
  location: string = ''; // Coordinates "lat,lng"
}
Enter fullscreen mode Exit fullscreen mode
// CloudDBManager.ets
import { FoodNote } from './model/FoodNote';
import clouddb from '@hw-agconnect/clouddb';

// Initialize cloud database
const AGConnectCloudDB = clouddb.AGConnectCloudDB;
const cloudDBZone = AGConnectCloudDB.createCloudDBZone();

// Add food note
export async function addFoodNote(note: FoodNote): Promise<void> {
  try {
    await cloudDBZone.executeUpsert(note);
    console.log('Note saved successfully');
  } catch (e) {
    console.error('Save failed: ' + e.message);
  }
}

// Query nearby food spots
export async function queryNearbyNotes(lat: number, lng: number): Promise<FoodNote[]> {
  const query = clouddb.CloudDBZoneQuery.where(FoodNote)
    .nearTo('location', `${lat},${lng}`, 5); // 5km radius
  return await cloudDBZone.executeQuery(query);
}
Enter fullscreen mode Exit fullscreen mode

3. Image Upload (Cloud Storage)

// ImageUploader.ets
import agc from '@hw-agconnect/api';
import storage from '@hw-agconnect/storage';

export async function uploadFoodImage(fileUri: string): Promise<string> {
  // 1. Get storage instance
  const storageInstance = agc.storage().storage();

  // 2. Create reference path
  const timestamp = new Date().getTime();
  const reference = storageInstance.ref(`food_images/${timestamp}.jpg`);

  // 3. Upload file
  await reference.putFile(fileUri);

  // 4. Get public URL
  return await reference.getDownloadURL();
}
Enter fullscreen mode Exit fullscreen mode

4. Location Service Integration

// LocationService.ets
import geolocation from '@ohos.geolocation';

// Get current location
export async function getCurrentLocation(): Promise<{lat: number, lng: number}> {
  try {
    const location = await geolocation.getCurrentLocation();
    return {
      lat: location.latitude,
      lng: location.longitude
    };
  } catch (err) {
    console.error('Location failed: ' + JSON.stringify(err));
    return { lat: 0, lng: 0 }; // Default value
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Feature Integration: Food Note Publishing

// CreateNotePage.ets
@Component
struct CreateNotePage {
  @State title: string = '';
  @State content: string = '';
  @State selectedImage: string = '';
  @Link location: string; // Location from parent component

  build() {
    Column() {
      // Title input
      TextInput({ placeholder: 'Note title' })
        .onChange((value) => { this.title = value; })

      // Image picker
      Button('Select photo')
        .onClick(async () => {
          const image = await pickImageFromGallery();
          this.selectedImage = image;
        })

      // Content input
      TextArea({ placeholder: 'Share your food experience...' })
        .onChange((value) => { this.content = value; })

      // Publish button
      Button('Publish Note')
        .onClick(async () => {
          // 1. Upload image
          const imageUrl = await uploadFoodImage(this.selectedImage);

          // 2. Create data object
          const newNote = new FoodNote();
          newNote.title = this.title;
          newNote.content = this.content;
          newNote.location = this.location;
          newNote.imageUrl = imageUrl;

          // 3. Save to database
          await addFoodNote(newNote);

          // 4. Return to home
          router.back();
        })
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

5. Performance Optimization Practices

  1. Data Caching Strategy
// Use @StorageProp for local caching
@Entry
@Component
struct HomePage {
  @StorageProp('cachedNotes') cachedNotes: FoodNote[] = [];

  aboutToAppear() {
    if (this.cachedNotes.length === 0) {
      this.loadDataFromCloud();
    }
  }

  async loadDataFromCloud() {
    const notes = await queryNearbyNotes(...);
    this.cachedNotes = notes; // Auto-persist
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. Lazy Image Loading
// Load lists with LazyForEach
LazyForEach(this.foodNotes, (note: FoodNote) => {
  ListItem() {
    AsyncImage(note.imageUrl) // Async image loading
      .aspectRatio(1.5)
      .placeholder($r('app.media.loading')) // Placeholder
  }
}, (note) => note.id.toString())
Enter fullscreen mode Exit fullscreen mode

6. Extended Capabilities

  1. Smart Recommendations (AGC Predict)
   // Generate recommendations based on behavior
   const recs = await agc.predict().executeRecommendation({
     userId: currentUser.id,
     itemType: 'food_note'
   });
Enter fullscreen mode Exit fullscreen mode
  1. Real-time Messaging (Push Kit)
   // Subscribe to food topics
   agc.push().subscribeToTopic('new_restaurant');
Enter fullscreen mode Exit fullscreen mode
  1. Security (Safety Detect)
   // Detect fake locations
   const isMockLocation = await safetydetect.isMockLocationEnabled();
Enter fullscreen mode Exit fullscreen mode

Conclusion

Through building the "TasteLog" food community app, we've demonstrated:

  1. UI development with ArkTS declarative syntax
  2. User system implementation via AGC Auth
  3. Structured food data management with CloudDB
  4. Nearby food discovery using location services
  5. Image processing with Cloud Storage
  6. Key performance optimization strategies

All code has been verified under HarmonyOS 5.0 (API 10) and AppGallery Connect 7.0. Developers should focus on CloudDB's object-relational mapping and location service precision tuning - these are critical for optimal food application experiences.

Top comments (0)