DEV Community

linzhongxue
linzhongxue

Posted on

Development Guide for Food Discovery Application Based on HarmonyOS Next

Development Guide for Food Discovery Application Based on HarmonyOS Next

1. Project Overview and Development Environment Setup

We will develop a food discovery app named "FoodFinder" with core features:

  1. Location-based nearby restaurant recommendations
  2. Food detail display and user ratings
  3. Favorites management
  4. Restaurant search and filtering

Development Environment Requirements:

  • DevEco Studio 4.1 (Build Version 4.1.3.400)
  • HarmonyOS SDK 5.0
  • AppGallery Connect service integration
  • TypeScript language environment

Project Initialization Steps:

  1. Create a HarmonyOS application project
  2. Configure cloud service dependencies in build-profile.json5
"dependencies": {  
  "@ohos/agconnect-database": "^5.0.0",  
  "@ohos/agconnect-auth": "^5.0.0",  
  "@ohos/location": "^5.0.0"  
}  
Enter fullscreen mode Exit fullscreen mode
  1. Enable CloudDB, Auth Service, and Location Service in AGC console

2. Core Function Implementation and Code Analysis

1. Location Acquisition and Restaurant Data Query

// Get user location and query nearby restaurants  
import geoLocationManager from '@ohos.geoLocationManager';  
import cloudDB from '@ohos/agconnect-database';  

@Entry  
@Component  
struct NearbyRestaurants {  
  @State restaurants: Restaurant[] = [];  

  async aboutToAppear() {  
    // Get current location  
    const location = await geoLocationManager.getCurrentLocation();  
    this.queryNearbyRestaurants(location.latitude, location.longitude);  
  }  

  // Query nearby restaurants  
  async queryNearbyRestaurants(lat: number, lon: number) {  
    const cloudDBZone = cloudDB.getCloudDBZone("FoodDB");  
    const query = cloudDB.createQuery();  
    query.equalTo('geoPoint', cloudDB.GeoPoint(lat, lon));  
    query.limit(10);  

    const snapshot = await cloudDBZone.executeQuery(query);  
    this.restaurants = snapshot.getObjects(Restaurant);  
  }  

  build() {  
    List() {  
      ForEach(this.restaurants, (item: Restaurant) => {  
        RestaurantItem({ data: item })  
      })  
    }  
  }  
}  

// Restaurant data model  
class Restaurant implements cloudDB.CloudDBZoneObject {  
  @cloudDB.field() id: string = '';  
  @cloudDB.field() name: string = '';  
  @cloudDB.field() rating: number = 0;  
  @cloudDB.field({ geoPoint: true }) location: cloudDB.GeoPoint = new cloudDB.GeoPoint(0, 0);  
}  
Enter fullscreen mode Exit fullscreen mode

2. Restaurant Detail Page Implementation

@Component  
struct RestaurantDetail {  
  @Prop restaurant: Restaurant;  
  @State isFavorite: boolean = false;  

  // Check if favorited  
  async aboutToAppear() {  
    const auth = await import('@ohos/agconnect-auth');  
    const userId = auth.getCurrentUser().uid;  
    this.isFavorite = await this.checkFavoriteStatus(userId, this.restaurant.id);  
  }  

  // Toggle favorite status  
  toggleFavorite() {  
    if (this.isFavorite) {  
      // Remove from CloudDB  
      cloudDB.getCloudDBZone("UserDB").delete(this.favoriteRecord);  
    } else {  
      // Add to CloudDB  
      const record = new FavoriteRecord(userId, this.restaurant.id);  
      cloudDB.getCloudDBZone("UserDB").upsert(record);  
    }  
    this.isFavorite = !this.isFavorite;  
  }  

  build() {  
    Column() {  
      Image(this.restaurant.mainImage)  
        .width('100%')  
        .aspectRatio(1.5)  

      Text(this.restaurant.name)  
        .fontSize(24)  
        .margin(10)

      RatingBar({ rating: this.restaurant.rating })

      Button(this.isFavorite ? 'Unfavorite' : 'Favorite')
        .onClick(() => this.toggleFavorite())
        .margin(20)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Favorites Synchronization

// User favorites management
class FavoriteService {
  // Get all user favorites
  static async getUserFavorites(userId: string): Promise<Restaurant[]> {
    const query = cloudDB.createQuery()
      .equalTo('userId', userId)
      .relatedTo('restaurant', Restaurant);

    const snapshot = await cloudDB.getCloudDBZone("UserDB").executeQuery(query);
    return snapshot.getObjects(Restaurant);
  }

  // Setup real-time data listener
  static setupFavoritesListener(callback: (favorites: Restaurant[]) => void) {
    const query = cloudDB.createQuery()
      .equalTo('userId', auth.getCurrentUser().uid);

    const listener = cloudDB.getCloudDBZone("UserDB")
      .subscribe(query, {
        onSnapshot: (snapshot) => {
          callback(snapshot.getObjects(Restaurant));
        }
      });
    return listener;
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Key HarmonyOS Features Implementation

1. Atomic Service for Dish Sharing

// Define sharing atomic service
@Entry
@Component
struct DishShareCard {
  @State dishInfo: Dish = new Dish();

  build() {
    Card() {
      Column() {
        Image(this.dishInfo.image)
          .width(120)
          .height(120)
        Text(this.dishInfo.name)
          .fontSize(16)
        Text(${this.dishInfo.price}`)
          .fontColor(Color.Red)
      }
      .padding(10)
    }
    .onClick(() => {
      // Navigate to restaurant detail
      router.pushUrl({ url: `pages/RestaurantDetail?id=${this.dishInfo.restaurantId}` });
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Offline Caching with Relational Database

// Initialize local database
const RDB_STORE_CONFIG: relationalStore.StoreConfig = {
  name: "FoodCache.db",
  securityLevel: relationalStore.SecurityLevel.S1
};

relationalStore.getRdbStore(this.context, RDB_STORE_CONFIG, (err, store) => {
  store.executeSql(
    `CREATE TABLE IF NOT EXISTS restaurant_cache (
      id TEXT PRIMARY KEY,
      name TEXT,
      rating REAL,
      data TEXT
    )`
  );
});

// Use cached data when network fails
async function getRestaurants() {
  try {
    const onlineData = await fetchOnlineData();
    cacheData(onlineData); // Update cache
    return onlineData;
  } catch (error) {
    return loadCachedData(); // Return cached data
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Performance Optimization Practices

  1. Image Loading Optimization
// Use lazy loading and placeholder
AsyncImage(this.restaurant.image)
  .placeholder($r('app.media.placeholder'))
  .transitionEffect(TransitionEffect.OPACITY)
Enter fullscreen mode Exit fullscreen mode
  1. Pagination Loading
// Implement scroll pagination
List({ scroller: this.scroller }) {
  // ...
}
.onReachEnd(() => {
  if (!this.isLoading) {
    this.loadNextPage();
  }
})
Enter fullscreen mode Exit fullscreen mode
  1. Rendering Performance Optimization
// Optimize long lists with component reuse
ForEach(this.restaurants, (item: Restaurant) => {
  RestaurantItem({ data: item })
}, (item: Restaurant) => item.id) // Key: set unique identifier
Enter fullscreen mode Exit fullscreen mode

5. AppGallery Connect Integration Key Points

  1. CloudDB Rule Configuration
{
  "rules": {
    "restaurants": {
      ".read": "auth != null",
      ".write": "auth != null && resource.data.owner == auth.uid"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. AB Testing for Recommendation Algorithm
// Get AB testing parameters
import agc from '@ohos/agconnect-remoteconfig';

const config = agc.getRemoteConfig();
const params = await config.applyDefaults({
  recommendation_algorithm: 'v1'
}).fetch();

const algorithmVersion = params.getValue('recommendation_algorithm').asString();
Enter fullscreen mode Exit fullscreen mode

6. Project Testing and Release

  1. Unit Test Example
// Test rating calculation logic
describe('RatingCalculator', () => {
  it('should calculate weighted rating', () => {
    const ratings = [5, 4, 3, 5];
    const result = calculateWeightedRating(ratings);
    expect(result).toBeCloseTo(4.25);
  });
});
Enter fullscreen mode Exit fullscreen mode
  1. Pre-release Checklist
  2. [ ] Complete AGC application signature configuration
  3. [ ] Pass ArkCompiler compilation check in DevEco Studio
  4. [ ] Test location services on Huawei devices
  5. [ ] Configure privacy policy documents

Conclusion

This guide demonstrates the complete development process of FoodFinder application using HarmonyOS Next capabilities:

  1. Implement cloud data synchronization with @ohos/agconnect-database
  2. Enhance UX with Atomic Services
  3. Enable offline capabilities with relational databases
  4. Achieve multi-device sync through distributed capabilities

Further expansions:

  • Integrate Huawei Pay for online ordering
  • Add AR menu preview functionality
  • Implement cross-device cooking guide handoff

Full source code: github.com/HarmonyOS-FoodFinder

Note: Configure CloudDB schema in AGC console and enable location permissions before running

Through this practice, developers can master the complete development workflow for lifestyle applications on HarmonyOS Next, leveraging distributed capabilities and cloud services to build high-performance applications.

Top comments (0)